diff --git a/Applications/PluginGenerator/PluginTemplate/documentation/UserManual/Manual.dox b/Applications/PluginGenerator/PluginTemplate/documentation/UserManual/Manual.dox index 8351db4f16..481dc52e49 100755 --- a/Applications/PluginGenerator/PluginTemplate/documentation/UserManual/Manual.dox +++ b/Applications/PluginGenerator/PluginTemplate/documentation/UserManual/Manual.dox @@ -1,18 +1,17 @@ /** -\page $(plugin-target) $(plugin-name) +\page $(plugin-target) The $(plugin-name) \imageMacro{icon.png,"Icon of $(plugin-name)",2.00} -Available sections: - - \ref $(plugin-target)Overview +\tableofcontents -\section $(plugin-target)Overview +\section $(plugin-target)Overview Overview Describe the features of your awesome plugin here */ diff --git a/BlueBerry/BlueBerryConfig.cmake.in b/BlueBerry/BlueBerryConfig.cmake.in index 5a5d8bbecf..342ae0a783 100644 --- a/BlueBerry/BlueBerryConfig.cmake.in +++ b/BlueBerry/BlueBerryConfig.cmake.in @@ -1,91 +1,91 @@ # ------------------------------------------------------------------------- # Package requirements # ------------------------------------------------------------------------- set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "@BLUEBERRY_SOURCE_DIR@/CMake") set(CTK_DIR "@CTK_DIR@") find_package(CTK REQUIRED) set(mbilog_DIR "@mbilog_DIR@") find_package(mbilog REQUIRED) find_package(Poco REQUIRED) set(DOXYGEN_EXECUTABLE "@DOXYGEN_EXECUTABLE@") set(DOXYGEN_DOT_EXECUTABLE "@DOXYGEN_DOT_EXECUTABLE@") find_package(Doxygen) # ------------------------------------------------------------------------- # BlueBerry directory vars # ------------------------------------------------------------------------- set(BLUEBERRY_PLUGINS_SOURCE_DIR "@BLUEBERRY_PLUGINS_SOURCE_DIR@") set(BLUEBERRY_PLUGINS_BINARY_DIR "@BLUEBERRY_PLUGINS_BINARY_DIR@") set(BLUEBERRY_PLUGIN_SOURCE_DIRS "@BLUEBERRY_PLUGINS_SOURCE_DIR@") set(BLUEBERRY_SOURCE_DIR "@BLUEBERRY_SOURCE_DIR@") set(BlueBerry_SOURCE_DIR "@BLUEBERRY_SOURCE_DIR@") set(BLUEBERRY_BINARY_DIR "@BLUEBERRY_BINARY_DIR@") # ------------------------------------------------------------------------- # BlueBerry CMake file includes # ------------------------------------------------------------------------- set(BLUEBERRY_PLUGIN_USE_FILE @BB_PLUGIN_USE_FILE@) if(BLUEBERRY_PLUGIN_USE_FILE) if(EXISTS ${BLUEBERRY_PLUGIN_USE_FILE}) include(${BLUEBERRY_PLUGIN_USE_FILE}) endif() endif() if(NOT BB_PLUGIN_EXPORTS_FILE_INCLUDED AND NOT CMAKE_PROJECT_NAME STREQUAL "MITK") include("@BB_PLUGIN_EXPORTS_FILE@") set(BB_PLUGIN_EXPORTS_FILE_INCLUDED 1) endif() # ------------------------------------------------------------------------- # BlueBerry CMake variables # ------------------------------------------------------------------------- set(BLUEBERRY_DEBUG_POSTFIX @BLUEBERRY_DEBUG_POSTFIX@) set(BLUEBERRY_USE_QT_HELP @BLUEBERRY_USE_QT_HELP@) -if(BLUEBERRY_USE_QT_HELP AND NOT DOXYGEN_VERSION VERSION_EQUAL "1.8.0") - message("Setting BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.0 not found.") +if(BLUEBERRY_USE_QT_HELP AND DOXYGEN_VERSION VERSION_LESS "1.8.7") + message("Setting BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(BLUEBERRY_USE_QT_HELP OFF) endif() set(BLUEBERRY_QTPLUGIN_PATH "@BLUEBERRY_QTPLUGIN_PATH@") set(QT_HELPGENERATOR_EXECUTABLE "@QT_HELPGENERATOR_EXECUTABLE@") set(QT_COLLECTIONGENERATOR_EXECUTABLE "@QT_COLLECTIONGENERATOR_EXECUTABLE@") set(QT_ASSISTANT_EXECUTABLE "@QT_ASSISTANT_EXECUTABLE@") set(QT_XMLPATTERNS_EXECUTABLE "@QT_XMLPATTERNS_EXECUTABLE@") set(BLUEBERRY_PLUGIN_TARGETS @my_plugin_targets@) set(BLUEBERRY_PLUGIN_PROVISIONING_FILE "@BLUEBERRY_PROVISIONING_FILE@") set(BLUEBERRY_DOXYGEN_TAGFILE_NAME @BLUEBERRY_DOXYGEN_TAGFILE_NAME@) # ------------------------------------------------------------------------- # BlueBerry CMake macros # ------------------------------------------------------------------------- include(MacroParseArguments) include(MacroConvertSchema) include(MacroOrganizeSources) include(MacroCreateCTKPlugin) include(MacroCreateQtHelp) include(MacroInstallCTKPlugin) include(FunctionInstallThirdPartyCTKPlugins) include(FunctionCreateProvisioningFile) include(FunctionCreateBlueBerryApplication) diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp b/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp index e6d42122c4..a82637097f 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp +++ b/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp @@ -1,246 +1,247 @@ /*=================================================================== BlueBerry Platform Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "berryQtStylePreferencePage.h" #include #include #include #include namespace berry { QtStylePreferencePage::QtStylePreferencePage() { } QtStylePreferencePage::QtStylePreferencePage(const QtStylePreferencePage& other) { Q_UNUSED(other) } void QtStylePreferencePage::Init(IWorkbench::Pointer ) { } void QtStylePreferencePage::CreateQtControl(QWidget* parent) { mainWidget = new QWidget(parent); controls.setupUi(mainWidget); berry::IPreferencesService::Pointer prefService = berry::Platform::GetServiceRegistry() .GetServiceById(berry::IPreferencesService::ID); styleManager = berry::Platform::GetServiceRegistry() .GetServiceById(berry::IQtStyleManager::ID); m_StylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); Update(); connect(controls.m_StylesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(StyleChanged(int))); connect(controls.m_IconThemeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(IconThemeChanged(int))); connect(controls.m_PathList, SIGNAL(itemSelectionChanged()), this, SLOT(UpdatePathListButtons())); connect(controls.m_AddButton, SIGNAL(clicked(bool)), this, SLOT(AddPathClicked(bool))); connect(controls.m_EditButton, SIGNAL(clicked(bool)), this, SLOT(EditPathClicked(bool))); connect(controls.m_RemoveButton, SIGNAL(clicked(bool)), this, SLOT(RemovePathClicked(bool))); } void QtStylePreferencePage::FillStyleCombo(const berry::IQtStyleManager::Style& currentStyle) { controls.m_StylesCombo->clear(); styles.clear(); styleManager->GetStyles(styles); qSort(styles); for (int i = 0; i < styles.size(); ++i) { controls.m_StylesCombo->addItem(styles.at(i).name, QVariant(styles.at(i).fileName)); } controls.m_StylesCombo->setCurrentIndex(styles.indexOf(currentStyle)); } -void QtStylePreferencePage::FillIconThemeComboBox() +void QtStylePreferencePage::FillIconThemeComboBox(const QString currentIconTheme) { controls.m_IconThemeComboBox->clear(); berry::IQtStyleManager::IconThemeList iconThemes; styleManager->GetIconThemes(iconThemes); qSort(iconThemes); for (int i = 0; i < iconThemes.size(); ++i) { controls.m_IconThemeComboBox->addItem(iconThemes.at(i).name); } - QString currentTheme = QIcon::themeName(); + QString currentTheme = currentIconTheme; + if(currentTheme == QString("")) { currentTheme = QString("<>"); } controls.m_IconThemeComboBox->setCurrentIndex(iconThemes.indexOf( currentTheme )); } void QtStylePreferencePage::AddPath(const QString& path, bool updateCombo) { if (!controls.m_PathList->findItems(path, Qt::MatchCaseSensitive).isEmpty()) return; new QListWidgetItem(path, controls.m_PathList); styleManager->AddStyles(path); if (updateCombo) FillStyleCombo(oldStyle); } void QtStylePreferencePage::StyleChanged(int /*index*/) { QString fileName = controls.m_StylesCombo->itemData(controls.m_StylesCombo->currentIndex()).toString(); styleManager->SetStyle(fileName); } void QtStylePreferencePage::IconThemeChanged(int /*index*/) { QString themeName = controls.m_IconThemeComboBox->currentText(); styleManager->SetIconTheme(themeName); } void QtStylePreferencePage::AddPathClicked(bool /*checked*/) { QListWidgetItem* item = controls.m_PathList->currentItem(); QString initialDir; if (item) initialDir = item->text(); QString dir = QFileDialog::getExistingDirectory(mainWidget, "", initialDir); if (!dir.isEmpty()) this->AddPath(dir, true); } void QtStylePreferencePage::RemovePathClicked(bool /*checked*/) { QList selection = controls.m_PathList->selectedItems(); QListIterator it(selection); while (it.hasNext()) { QListWidgetItem* item = it.next(); QString dir = item->text(); controls.m_PathList->takeItem(controls.m_PathList->row(item)); delete item; styleManager->RemoveStyles(dir); } if (!styleManager->Contains(oldStyle.fileName)) { oldStyle = styleManager->GetDefaultStyle(); } FillStyleCombo(oldStyle); } void QtStylePreferencePage::EditPathClicked(bool checked) { QListWidgetItem* item = controls.m_PathList->currentItem(); QString initialDir = item->text(); QString dir = QFileDialog::getExistingDirectory(mainWidget, "", initialDir); if (!dir.isEmpty()) { this->RemovePathClicked(checked); this->AddPath(dir, true); } } void QtStylePreferencePage::UpdatePathListButtons() { int s = controls.m_PathList->selectedItems().size(); if (s == 0) { controls.m_EditButton->setEnabled(false); controls.m_RemoveButton->setEnabled(false); } else if (s == 1) { controls.m_EditButton->setEnabled(true); controls.m_RemoveButton->setEnabled(true); } else { controls.m_EditButton->setEnabled(false); controls.m_RemoveButton->setEnabled(true); } } QWidget* QtStylePreferencePage::GetQtControl() const { return mainWidget; } bool QtStylePreferencePage::PerformOk() { m_StylePref->Put(berry::QtPreferences::QT_STYLE_NAME, controls.m_StylesCombo->itemData(controls.m_StylesCombo->currentIndex()).toString().toStdString()); std::string paths; for (int i = 0; i < controls.m_PathList->count(); ++i) { QString path = controls.m_PathList->item(i)->text() + ";"; paths += path.toStdString(); } QString currentTheme = QIcon::themeName(); if(currentTheme == QString("")) { currentTheme = QString("<>"); } m_StylePref->Put(berry::QtPreferences::QT_STYLE_SEARCHPATHS, paths); m_StylePref->Put(berry::QtPreferences::QT_ICON_THEME, currentTheme.toStdString()); return true; } void QtStylePreferencePage::PerformCancel() { Update(); } void QtStylePreferencePage::Update() { styleManager->RemoveStyles(); QString paths = QString::fromStdString(m_StylePref->Get(berry::QtPreferences::QT_STYLE_SEARCHPATHS, "")); QStringList pathList = paths.split(";", QString::SkipEmptyParts); QStringListIterator it(pathList); while (it.hasNext()) { AddPath(it.next(), false); } QString iconTheme = QString::fromStdString(m_StylePref->Get(berry::QtPreferences::QT_ICON_THEME, "<>")); styleManager->SetIconTheme( iconTheme ); std::string name = m_StylePref->Get(berry::QtPreferences::QT_STYLE_NAME, ""); styleManager->SetStyle(QString::fromStdString(name)); oldStyle = styleManager->GetStyle(); FillStyleCombo(oldStyle); - FillIconThemeComboBox(); + FillIconThemeComboBox( iconTheme ); } } diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.h b/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.h index 124458db09..8568d6f300 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.h +++ b/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.h @@ -1,83 +1,83 @@ /*=================================================================== BlueBerry Platform Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef BERRYQTSTYLEPREFERENCEPAGE_H_ #define BERRYQTSTYLEPREFERENCEPAGE_H_ #include #include #include #include #include namespace berry { class QtStylePreferencePage : public QObject, public IQtPreferencePage { Q_OBJECT Q_INTERFACES(berry::IPreferencePage) public: QtStylePreferencePage(); QtStylePreferencePage(const QtStylePreferencePage& other); void Init(IWorkbench::Pointer workbench); void CreateQtControl(QWidget* parent); QWidget* GetQtControl() const; bool PerformOk(); void PerformCancel(); void Update(); protected: void AddPath(const QString& path, bool updateCombo); void FillStyleCombo(const berry::IQtStyleManager::Style& currentStyle); - void FillIconThemeComboBox(); + void FillIconThemeComboBox(const QString currentIconTheme); protected slots: void StyleChanged(int /*index*/); void IconThemeChanged(int /*index*/); void AddPathClicked(bool /*checked*/); void RemovePathClicked(bool /*checked*/); void EditPathClicked(bool checked); void UpdatePathListButtons(); private: berry::IQtStyleManager::Pointer styleManager; Ui::QtStylePreferencePageUI controls; berry::IPreferences::Pointer m_StylePref; berry::IQtStyleManager::Style oldStyle; berry::IQtStyleManager::StyleList styles; QWidget* mainWidget; }; } #endif /* BERRYQTSTYLEPREFERENCEPAGE_H_ */ diff --git a/BlueBerry/CMakeLists.txt b/BlueBerry/CMakeLists.txt index 75db3b02a0..3b77eba726 100644 --- a/BlueBerry/CMakeLists.txt +++ b/BlueBerry/CMakeLists.txt @@ -1,285 +1,285 @@ project(BlueBerry) if (${CMAKE_SOURCE_DIR} EQUAL ${PROJECT_SOURCE_DIR}) cmake_minimum_required(VERSION 2.8.9) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/CMake/") include(MacroParseArguments) include(MacroConvertSchema) include(MacroOrganizeSources) include(MacroCreateCTKPlugin) include(MacroCreateQtHelp) include(MacroInstallCTKPlugin) include(FunctionCreateProvisioningFile) if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOMINMAX /wd4250 /wd4275 /wd4251 /wd4503") endif() if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) endif() find_package(mbilog REQUIRED) include_directories(${mbilog_INCLUDE_DIRS}) if(MITK_USE_Qt4) find_package(Qt4 4.7 REQUIRED) else() find_package(Qt5Concurrent ${MITK_QT5_MINIMUM_VERSION} REQUIRED) find_package(Qt5Help ${MITK_QT5_MINIMUM_VERSION} REQUIRED) find_package(Qt5Sql ${MITK_QT5_MINIMUM_VERSION} REQUIRED) find_package(Qt5WebKitWidgets ${MITK_QT5_MINIMUM_VERSION} REQUIRED) find_package(Qt5Widgets ${MITK_QT5_MINIMUM_VERSION} REQUIRED) find_package(Qt5Xml ${MITK_QT5_MINIMUM_VERSION} REQUIRED) endif() if(QT_QMAKE_CHANGED) set(QT_HELPGENERATOR_EXECUTABLE NOTFOUND) set(QT_COLLECTIONGENERATOR_EXECUTABLE NOTFOUND) set(QT_ASSISTANT_EXECUTABLE NOTFOUND) set(QT_XMLPATTERNS_EXECUTABLE NOTFOUND) endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt4 qhelpgenerator4 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt4 qcollectiongenerator4 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant-qt4 assistant4 assistant PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_package(Doxygen) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating bundle documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE) if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() - if(DOXYGEN_FOUND AND NOT DOXYGEN_VERSION VERSION_EQUAL 1.8.0) - message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.0 not found.") + if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) + message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) endif() endif(BLUEBERRY_USE_QT_HELP) if(MITK_USE_Qt4) include(${QT_USE_FILE}) endif() # ========= CTK specific CMake stuff ============ cmake_policy(SET CMP0012 NEW) find_package(CTK REQUIRED) # Extract all library names starting with org_blueberry_ macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin OUTPUT_VARIABLE ${varname}) endmacro() # ================================================ option(BLUEBERRY_BUILD_ALL_PLUGINS "Build all BlueBerry plugins (overriding selection)" OFF) mark_as_advanced(BLUEBERRY_BUILD_ALL_PLUGINS) if(BLUEBERRY_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() option(BLUEBERRY_STATIC "Build all plugins as static libraries" OFF) mark_as_advanced(BLUEBERRY_STATIC) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) find_package(Poco REQUIRED) find_package(Ant) find_package(Eclipse) set(BLUEBERRY_SOURCE_DIR ${BlueBerry_SOURCE_DIR}) set(BLUEBERRY_BINARY_DIR ${BlueBerry_BINARY_DIR}) set(BLUEBERRY_PLUGINS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Bundles) set(BLUEBERRY_PLUGINS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Bundles) set(OSGI_APP solstice) set(OSGI_UI_APP solstice_ui) if(Eclipse_DIR) set(BLUEBERRY_DOC_TOOLS_DIR "${Eclipse_DIR}" CACHE PATH "Directory containing additional tools needed for generating the documentation") else() set(BLUEBERRY_DOC_TOOLS_DIR "" CACHE PATH "Directory containing additional tools needed for generating the documentation") endif() set(BLUEBERRY_DEBUG_POSTFIX d) # Testing options if(DEFINED BLUEBERRY_BUILD_TESTING) option(BLUEBERRY_BUILD_TESTING "Build the BlueBerry tests." ${BLUEBERRY_BUILD_TESTING}) else() option(BLUEBERRY_BUILD_TESTING "Build the BlueBerry tests." ${BUILD_TESTING}) endif() if(WIN32) set(_gui_testing_default "ON") else() set(_gui_testing_default "OFF") endif() option(BLUEBERRY_ENABLE_GUI_TESTING "Enable the BlueBerry GUI tests" ${_gui_testing_default}) mark_as_advanced(BLUEBERRY_ENABLE_GUI_TESTING) if(BLUEBERRY_BUILD_TESTING) enable_testing() endif() # Add CTK plugins set(_ctk_plugins Bundles/org.blueberry.osgi:ON Bundles/org.blueberry.compat:OFF Bundles/org.blueberry.core.runtime:OFF Bundles/org.blueberry.core.expressions:OFF Bundles/org.blueberry.solstice.common:OFF Bundles/org.blueberry.core.commands:OFF Bundles/org.blueberry.core.jobs:OFF Bundles/org.blueberry.ui:OFF Bundles/org.blueberry.ui.qt:OFF Bundles/org.blueberry.ui.qt.help:OFF Bundles/org.blueberry.ui.qt.log:ON Bundles/org.blueberry.ui.qt.objectinspector:OFF ) set(_ctk_test_plugins ) set(_ctk_plugins_include_dirs ${Poco_INCLUDE_DIRS} ) include_directories(${_ctk_plugins_include_dirs}) if(BLUEBERRY_BUILD_TESTING) find_package(CppUnit REQUIRED) include(berryTestingHelpers) set(BLUEBERRY_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_APP}") get_target_property(_is_macosx_bundle ${OSGI_APP} MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_APP}.app/Contents/MacOS/${OSGI_APP}") endif() set(_ctk_testinfrastructure_plugins Bundles/org.blueberry.test:ON Bundles/org.blueberry.uitest:ON ) set(_ctk_test_plugins # Testing/org.blueberry.core.runtime.tests:ON # Testing/org.blueberry.osgi.tests:ON ) if(BLUEBERRY_ENABLE_GUI_TESTING) # list(APPEND _ctk_test_plugins Testing/org.blueberry.ui.tests:ON) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_UI_APP}") get_target_property(_is_macosx_bundle ${OSGI_UI_APP} MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_UI_APP}.app/Contents/MacOS/${OSGI_UI_APP}") endif() endif() endif() set(BLUEBERRY_TESTING_PROVISIONING_FILE "${BlueBerry_BINARY_DIR}/BlueBerryTesting.provisioning") add_custom_target(BlueBerry) ctkMacroSetupPlugins(${_ctk_plugins} ${_ctk_testinfrastructure_plugins} ${_ctk_test_plugins} BUILD_OPTION_PREFIX BLUEBERRY_BUILD_ BUILD_ALL ${BLUEBERRY_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) set(BLUEBERRY_PROVISIONING_FILE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/BlueBerry.provisioning") FunctionCreateProvisioningFile( FILE ${BLUEBERRY_PROVISIONING_FILE} PLUGINS ${_ctk_plugins} ) FunctionCreateProvisioningFile( FILE ${BLUEBERRY_TESTING_PROVISIONING_FILE} INCLUDE ${BLUEBERRY_PROVISIONING_FILE} PLUGINS ${_ctk_testinfrastructure_plugins} ${_ctk_test_plugins} ) if(${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES) add_dependencies(BlueBerry ${${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES}) endif() set_property(TARGET ${${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES} PROPERTY LABELS BlueBerry) set(BB_PLUGIN_USE_FILE "${BlueBerry_BINARY_DIR}/BlueBerryPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${BB_PLUGIN_USE_FILE}) else() file(REMOVE ${BB_PLUGIN_USE_FILE}) set(BB_PLUGIN_USE_FILE ) endif() # CTK Plugin Exports set(BB_PLUGIN_EXPORTS_FILE "${CMAKE_CURRENT_BINARY_DIR}/BlueBerryPluginExports.cmake") GetMyTargetLibraries("${${PROJECT_NAME}_PLUGIN_LIBRARIES}" my_plugin_targets) set(additional_export_targets mbilog) export(TARGETS ${my_plugin_targets} ${additional_export_targets} FILE ${BB_PLUGIN_EXPORTS_FILE}) add_subdirectory(Documentation) set(BLUEBERRY_QTPLUGIN_PATH ) if(CTK_QTDESIGNERPLUGINS_DIR AND EXISTS ${CTK_QTDESIGNERPLUGINS_DIR}) set(BLUEBERRY_QTPLUGIN_PATH "${CTK_QTDESIGNERPLUGINS_DIR}") endif() configure_file(BlueBerryConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/BlueBerryConfig.cmake @ONLY) diff --git a/CMake/MITKDashboardDriverScript.cmake b/CMake/MITKDashboardDriverScript.cmake index 2c75718c26..4c36060d43 100644 --- a/CMake/MITKDashboardDriverScript.cmake +++ b/CMake/MITKDashboardDriverScript.cmake @@ -1,484 +1,485 @@ # # Included from a dashboard script, this cmake file will drive the configure and build # steps of MITK # #----------------------------------------------------------------------------- # The following variable are expected to be define in the top-level script: set(expected_variables CTEST_NOTES_FILES CTEST_SITE CTEST_DASHBOARD_ROOT CTEST_CMAKE_COMMAND CTEST_CMAKE_GENERATOR WITH_MEMCHECK WITH_COVERAGE WITH_DOCUMENTATION CTEST_BUILD_CONFIGURATION CTEST_TEST_TIMEOUT CTEST_BUILD_FLAGS TEST_TO_EXCLUDE_REGEX CTEST_SOURCE_DIRECTORY CTEST_BINARY_DIRECTORY CTEST_BUILD_NAME SCRIPT_MODE CTEST_COVERAGE_COMMAND CTEST_MEMORYCHECK_COMMAND CTEST_GIT_COMMAND PROJECT_BUILD_DIR SUPERBUILD_TARGETS ) foreach(var ${expected_variables}) if(NOT DEFINED ${var}) message(FATAL_ERROR "Variable ${var} should be defined in top-level script !") endif() endforeach() # Check if "mbits" is reachable file(DOWNLOAD "http://mbits" "${CTEST_SCRIPT_DIRECTORY}/mbits.html" TIMEOUT 2 STATUS _status) list(GET _status 0 _status_code) if(_status_code EQUAL 6) # couldn't resovle host name set(MBITS_AVAILABLE 0) else() set(MBITS_AVAILABLE 1) endif() # # Site specific options # if(NOT CDASH_ADMIN_URL_PREFIX AND MBITS_AVAILABLE) set(CDASH_ADMIN_URL_PREFIX "http://mbits") endif() if(NOT MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL AND MBITS_AVAILABLE) set(MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL "http://mbits/dl-cache") endif() if(NOT DEFINED GIT_BRANCH OR GIT_BRANCH STREQUAL "") set(GIT_BRANCH "") else() set(GIT_BRANCH "-b ${GIT_BRANCH}") endif() # Should binary directory be cleaned? set(empty_binary_directory FALSE) # Attempt to build and test also if 'ctest_update' returned an error set(initial_force_build FALSE) # Set model options set(model "") if(SCRIPT_MODE STREQUAL "experimental") set(empty_binary_directory FALSE) set(initial_force_build TRUE) set(model Experimental) elseif(SCRIPT_MODE STREQUAL "continuous") set(empty_binary_directory FALSE) set(initial_force_build FALSE) set(model Continuous) elseif(SCRIPT_MODE STREQUAL "nightly") set(empty_binary_directory TRUE) set(initial_force_build TRUE) set(model Nightly) else() message(FATAL_ERROR "Unknown script mode: '${SCRIPT_MODE}'. Script mode should be either 'experimental', 'continuous' or 'nightly'") endif() #message("script_mode:${SCRIPT_MODE}") #message("model:${model}") #message("empty_binary_directory:${empty_binary_directory}") #message("force_build:${initial_force_build}") set(CTEST_CONFIGURATION_TYPE ${CTEST_BUILD_CONFIGURATION}) if(empty_binary_directory) message("Directory ${CTEST_BINARY_DIRECTORY} cleaned !") ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY}) endif() if(NOT DEFINED CTEST_CHECKOUT_DIR) set(CTEST_CHECKOUT_DIR ${CTEST_SOURCE_DIRECTORY}) endif() if(NOT EXISTS "${CTEST_CHECKOUT_DIR}") set(CTEST_CHECKOUT_COMMAND "\"${CTEST_GIT_COMMAND}\" clone ${GIT_BRANCH} ${GIT_REPOSITORY} \"${CTEST_CHECKOUT_DIR}\"") endif() set(CTEST_UPDATE_TYPE "git") set(CTEST_UPDATE_COMMAND "${CTEST_GIT_COMMAND}") #---------------------------------------------------------------------- # Utility macros #---------------------------------------------------------------------- function(func_build_target target build_dir) set(CTEST_BUILD_TARGET ${target}) ctest_build(BUILD "${build_dir}" APPEND RETURN_VALUE res NUMBER_ERRORS num_errors NUMBER_WARNINGS num_warnings) ctest_submit(PARTS Build RETRY_DELAY 3 RETRY_COUNT 3) if(num_errors) math(EXPR build_errors "${build_errors} + ${num_errors}") set(build_errors ${build_errors} PARENT_SCOPE) endif() if(num_warnings) math(EXPR build_warnings "${build_warnings} + ${num_warnings}") set(build_warnings ${build_warnings} PARENT_SCOPE) endif() endfunction() function(func_test label build_dir) if(NOT TESTING_PARALLEL_LEVEL) set(TESTING_PARALLEL_LEVEL 8) endif() if(label MATCHES "Unlabeled") set(_include_label EXCLUDE_LABEL .*) else() set(_include_label INCLUDE_LABEL ${label}) endif() ctest_test(BUILD "${build_dir}" ${_include_label} PARALLEL_LEVEL ${TESTING_PARALLEL_LEVEL} EXCLUDE ${TEST_TO_EXCLUDE_REGEX} RETURN_VALUE res ) ctest_submit(PARTS Test RETRY_DELAY 3 RETRY_COUNT 3) if(res) math(EXPR test_errors "${test_errors} + 1") set(test_errors ${test_errors} PARENT_SCOPE) endif() if(ARG3) set(WITH_COVERAGE ${ARG3}) endif() if(ARG4) set(WITH_MEMCHECK ${ARG4}) endif() if(WITH_COVERAGE AND CTEST_COVERAGE_COMMAND) message("----------- [ Coverage ${label} ] -----------") ctest_coverage(BUILD "${build_dir}" LABELS ${label}) ctest_submit(PARTS Coverage RETRY_DELAY 3 RETRY_COUNT 3) endif() if(WITH_MEMCHECK AND CTEST_MEMORYCHECK_COMMAND) if(NOT CTEST_MEMORYCHECK_SUPPRESSIONS_FILE) if(EXISTS "${CTEST_SOURCE_DIRECTORY}/CMake/valgrind.supp") set(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE "${CTEST_SOURCE_DIRECTORY}/CMake/valgrind.supp") endif() endif() if(NOT CTEST_MEMORYCHECK_COMMAND_OPTIONS) set(CTEST_MEMORYCHECK_COMMAND_OPTIONS "-q --tool=memcheck --leak-check=yes --show-reachable=no --show-possibly-lost=no --workaround-gcc296-bugs=yes --num-callers=50") endif() ctest_memcheck(BUILD "${build_dir}" INCLUDE_LABEL ${label}) ctest_submit(PARTS MemCheck RETRY_DELAY 3 RETRY_COUNT 3) endif() endfunction() #--------------------------------------------------------------------- # run_ctest macro #--------------------------------------------------------------------- macro(run_ctest) set(build_warnings 0) set(build_errors 0) set(test_errors 0) ctest_start(${model}) ctest_update(SOURCE "${CTEST_CHECKOUT_DIR}" RETURN_VALUE res) if(res LESS 0) # update error math(EXPR build_errors "${build_errors} + 1") endif() set(force_build ${initial_force_build}) # Check if a forced run was requested set(cdash_remove_rerun_url ) if(CDASH_ADMIN_URL_PREFIX) set(cdash_rerun_url "${CDASH_ADMIN_URL_PREFIX}/rerun/${CTEST_BUILD_NAME}") set(cdash_remove_rerun_url "${CDASH_ADMIN_URL_PREFIX}/rerun/rerun.php?name=${CTEST_BUILD_NAME}&remove=1") file(DOWNLOAD "${cdash_rerun_url}" "${CTEST_BINARY_DIRECTORY}/tmp.txt" STATUS status ) list(GET status 0 error_code) file(READ "${CTEST_BINARY_DIRECTORY}/tmp.txt" rerun_content LIMIT 1) if(NOT error_code AND NOT rerun_content) set(force_build 1) endif() endif() if(COMMAND MITK_OVERRIDE_FORCE_BUILD) MITK_OVERRIDE_FORCE_BUILD(force_build) endif() # force a build if this is the first run and the build dir is empty if(NOT EXISTS "${CTEST_BINARY_DIRECTORY}/CMakeCache.txt") message("First time build - Initialize CMakeCache.txt") set(res 1) # Write initial cache. if(NOT DEFINED BUILD_TESTING) set(BUILD_TESTING ON) endif() # Write initial cache. file(WRITE "${CTEST_BINARY_DIRECTORY}/CMakeCache.txt" " CTEST_USE_LAUNCHERS:BOOL=${CTEST_USE_LAUNCHERS} CTEST_PROJECT_ADDITIONAL_TARGETS:INTERNAL=${CTEST_PROJECT_ADDITIONAL_TARGETS} BUILD_TESTING:BOOL=${BUILD_TESTING} MITK_CTEST_SCRIPT_MODE:STRING=${SCRIPT_MODE} CMAKE_BUILD_TYPE:STRING=${CTEST_BUILD_CONFIGURATION} WITH_COVERAGE:BOOL=${WITH_COVERAGE} MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL:STRING=${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL} ${INITIAL_CMAKECACHE_OPTIONS} ") endif() if(res GREATER 0 OR force_build) # Clear the forced rerun request if(CDASH_ADMIN_URL_PREFIX AND cdash_remove_rerun_url) file(DOWNLOAD "${cdash_remove_rerun_url}" "${CTEST_BINARY_DIRECTORY}/tmp.txt") file(REMOVE "${CTEST_BINARY_DIRECTORY}/tmp.txt") endif() if(CTEST_PROJECT_NAME_SUPERBUILD) set(ctest_project_name_orig ${CTEST_PROJECT_NAME}) set(CTEST_PROJECT_NAME ${CTEST_PROJECT_NAME_SUPERBUILD}) endif() message("----------- [ Configure SuperBuild ] -----------") set_property(GLOBAL PROPERTY SubProject SuperBuild) set_property(GLOBAL PROPERTY Label SuperBuild) ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res OPTIONS "${SUPERBUILD_CONFIG_OPTIONS}") if(res) math(EXPR build_errors "${build_errors} + 1") endif() # Project.xml is generated during the superbuild configure step ctest_submit(FILES "${CTEST_BINARY_DIRECTORY}/Project.xml" RETRY_DELAY 3 RETRY_COUNT 3) ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}") ctest_submit(PARTS Configure RETRY_DELAY 3 RETRY_COUNT 3) # submit the update results *after* the submitting the Configure info, # otherwise CDash is somehow confused and cannot add the update info # to the superbuild project ctest_submit(PARTS Update RETRY_DELAY 3 RETRY_COUNT 3) # To get CTEST_PROJECT_SUBPROJECTS and CTEST_PROJECT_EXTERNALS definition include("${CTEST_BINARY_DIRECTORY}/CTestConfigSubProject.cmake") # Build top level (either all or the supplied targets at # superbuild level if(SUPERBUILD_TARGETS AND NOT build_errors) foreach(superbuild_target ${SUPERBUILD_TARGETS}) message("----------- [ Build ${superbuild_target} - SuperBuild ] -----------") func_build_target(${superbuild_target} "${CTEST_BINARY_DIRECTORY}") # runs only tests that have a LABELS property matching "SuperBuild" func_test("SuperBuild" "${CTEST_BINARY_DIRECTORY}") endforeach() # HACK Unfortunately ctest_coverage ignores the build argument, back-up the original dirs file(READ "${CTEST_BINARY_DIRECTORY}/CMakeFiles/TargetDirectories.txt" old_coverage_dirs) # explicitly build requested external projects as subprojects foreach(external_project_with_build_dir ${CTEST_PROJECT_EXTERNALS}) string(REPLACE "^^" ";" external_project_with_build_dir_list "${external_project_with_build_dir}") list(GET external_project_with_build_dir_list 0 external_project_name) list(GET external_project_with_build_dir_list 1 external_project_builddir) set_property(GLOBAL PROPERTY SubProject ${external_project_name}) set_property(GLOBAL PROPERTY Label ${external_project_name}) message("----------- [ Build ${external_project_name} ] -----------") func_build_target("${external_project_name}" "${CTEST_BINARY_DIRECTORY}") if(NOT build_errors) # HACK Unfortunately ctest_coverage ignores the build argument, try to force it... file(READ "${CTEST_BINARY_DIRECTORY}/${external_project_builddir}/CMakeFiles/TargetDirectories.txt" mitk_build_coverage_dirs) file(APPEND "${CTEST_BINARY_DIRECTORY}/CMakeFiles/TargetDirectories.txt" "${mitk_build_coverage_dirs}") message("----------- [ Test ${external_project_name} ] -----------") # runs only tests that have a LABELS property matching "${external_project_name}" func_test(${external_project_name} "${CTEST_BINARY_DIRECTORY}/${external_project_builddir}") # restore old coverage dirs file(WRITE "${CTEST_BINARY_DIRECTORY}/CMakeFiles/TargetDirectories.txt" "${old_coverage_dirs}") endif() endforeach() # switch back to SuperBuild label set_property(GLOBAL PROPERTY SubProject SuperBuild) set_property(GLOBAL PROPERTY Label SuperBuild) message("----------- [ Finish SuperBuild ] -----------") else() message("----------- [ Build SuperBuild ] -----------") endif() if(NOT build_errors) # build everything at superbuild level which has not yet been built func_build_target("" "${CTEST_BINARY_DIRECTORY}") endif() # runs only tests that have a LABELS property matching "SuperBuild" #func_test("SuperBuild" "${CTEST_BINARY_DIRECTORY}") set(build_dir "${CTEST_BINARY_DIRECTORY}/${PROJECT_BUILD_DIR}") if(CTEST_PROJECT_NAME_SUPERBUILD) set(CTEST_PROJECT_NAME ${ctest_project_name_orig}) endif() message("----------- [ Configure ${build_dir} ] -----------") # Configure target ctest_configure(BUILD "${build_dir}" OPTIONS "-DCTEST_USE_LAUNCHERS=${CTEST_USE_LAUNCHERS}" RETURN_VALUE res ) ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}") ctest_submit(PARTS Configure RETRY_DELAY 3 RETRY_COUNT 3) if(res) math(EXPR build_errors "${build_errors} + 1") endif() foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT build_errors) set_property(GLOBAL PROPERTY SubProject ${subproject}) set_property(GLOBAL PROPERTY Label ${subproject}) if(subproject MATCHES "Unlabeled") message("----------- [ Build All (Unlabeled) ] -----------") # Build target func_build_target("" "${build_dir}") else() message("----------- [ Build ${subproject} ] -----------") # Build target func_build_target(${subproject} "${build_dir}") endif() endif() endforeach() if(NOT build_errors) # HACK Unfortunately ctest_coverage ignores the build argument, try to force it... file(READ ${build_dir}/CMakeFiles/TargetDirectories.txt mitk_build_coverage_dirs) file(APPEND "${CTEST_BINARY_DIRECTORY}/CMakeFiles/TargetDirectories.txt" "${mitk_build_coverage_dirs}") foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) set_property(GLOBAL PROPERTY SubProject ${subproject}) set_property(GLOBAL PROPERTY Label ${subproject}) message("----------- [ Test ${subproject} ] -----------") # runs only tests that have a LABELS property matching "${subproject}" func_test(${subproject} "${build_dir}") endforeach() endif() # Build any additional target which is not build by "all" # i.e. the "package" target if(CTEST_PROJECT_ADDITIONAL_TARGETS AND NOT build_errors) foreach(additional_target ${CTEST_PROJECT_ADDITIONAL_TARGETS}) set_property(GLOBAL PROPERTY SubProject ${additional_target}) set_property(GLOBAL PROPERTY Label ${additional_target}) message("----------- [ Build ${additional_target} ] -----------") func_build_target(${additional_target} "${build_dir}") message("----------- [ Test ${additional_target} ] -----------") # runs only tests that have a LABELS property matching "${subproject}" func_test(${additional_target} "${build_dir}") endforeach() endif() if(WITH_DOCUMENTATION) message("----------- [ Build Documentation ] -----------") set(ctest_use_launchers_orig ${CTEST_USE_LAUNCHERS}) set(CTEST_USE_LAUNCHERS 0) # Build Documentation target set_property(GLOBAL PROPERTY SubProject Documentation) set_property(GLOBAL PROPERTY Label Documentation) func_build_target("doc" "${build_dir}") set(CTEST_USE_LAUNCHERS ${ctest_use_launchers_orig}) endif() set_property(GLOBAL PROPERTY SubProject SuperBuild) set_property(GLOBAL PROPERTY Label SuperBuild) # Global coverage ... if(WITH_COVERAGE AND CTEST_COVERAGE_COMMAND) message("----------- [ Global coverage ] -----------") ctest_coverage(BUILD "${build_dir}" APPEND) ctest_submit(PARTS Coverage RETRY_DELAY 3 RETRY_COUNT 3) endif() # Global dynamic analysis ... if(WITH_MEMCHECK AND CTEST_MEMORYCHECK_COMMAND) message("----------- [ Global memcheck ] -----------") ctest_memcheck(BUILD "${build_dir}") ctest_submit(PARTS MemCheck RETRY_DELAY 3 RETRY_COUNT 3) endif() # Note should be at the end ctest_submit(PARTS Notes RETRY_DELAY 3 RETRY_COUNT 3) # Send status to the "CDash Web Admin" if(CDASH_ADMIN_URL_PREFIX) set(cdash_admin_url "${CDASH_ADMIN_URL_PREFIX}/cdashadmin-web/index.php?pw=4da12ca9c06d46d3171d7f73974c900f") string(REGEX REPLACE ".*\\?project=(.*)&?" "\\1" _ctest_project "${CTEST_DROP_LOCATION}") + string(REPLACE " " "%20" _build_name_escaped "${CTEST_BUILD_NAME}") file(DOWNLOAD - "${cdash_admin_url}&action=submit&name=${CTEST_BUILD_NAME}&hasTestErrors=${test_errors}&hasBuildErrors=${build_errors}&hasBuildWarnings=${build_warnings}&ctestDropSite=${CTEST_DROP_SITE}&ctestProject=${_ctest_project}" + "${cdash_admin_url}&action=submit&name=${_build_name_escaped}&hasTestErrors=${test_errors}&hasBuildErrors=${build_errors}&hasBuildWarnings=${build_warnings}&ctestDropSite=${CTEST_DROP_SITE}&ctestProject=${_ctest_project}" "${CTEST_BINARY_DIRECTORY}/cdashadmin.txt" STATUS status ) list(GET status 0 error_code) list(GET status 1 error_msg) if(error_code) message(FATAL_ERROR "error: Failed to communicate with cdashadmin-web - ${error_msg}") endif() endif() endif() # Clear the CTEST_CHECKOUT_COMMAND variable to prevent continuous clients # to try to checkout again set(CTEST_CHECKOUT_COMMAND "") endmacro() if(SCRIPT_MODE STREQUAL "continuous") while(1) run_ctest() # Loop no faster than once every 5 minutes message("Wait for 5 minutes ...") ctest_sleep(300) endwhile() else() run_ctest() endif() diff --git a/CMake/MITKDashboardSetup.cmake b/CMake/MITKDashboardSetup.cmake index 59a8d0eb88..2c544532fb 100644 --- a/CMake/MITKDashboardSetup.cmake +++ b/CMake/MITKDashboardSetup.cmake @@ -1,206 +1,207 @@ # This file is intended to be included at the end of a custom MITKDashboardScript.TEMPLATE.cmake file list(APPEND CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") # # Automatically determined properties # set(MY_OPERATING_SYSTEM ) if(UNIX) # Download a utility script set(url "http://mitk.org/git/?p=MITK.git;a=blob_plain;f=CMake/mitkDetectOS.sh;hb=${hb}") set(dest "${CTEST_SCRIPT_DIRECTORY}/mitkDetectOS.sh") downloadFile("${url}" "${dest}") execute_process(COMMAND sh "${dest}" RESULT_VARIABLE _result OUTPUT_VARIABLE _out OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT _result) set(MY_OPERATING_SYSTEM "${_out}") endif() endif() if(NOT MY_OPERATING_SYSTEM) set(MY_OPERATING_SYSTEM "${CMAKE_HOST_SYSTEM}") # Windows 7, Linux-2.6.32, Darwin... endif() site_name(CTEST_SITE) if(NOT DEFINED MITK_USE_QT) set(MITK_USE_QT 1) endif() if(MITK_USE_QT) if(NOT QT_QMAKE_EXECUTABLE) find_program(QT_QMAKE_EXECUTABLE NAMES qmake qmake-qt4 HINTS ${QT_BINARY_DIR}) endif() execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} --version OUTPUT_VARIABLE MY_QT_VERSION RESULT_VARIABLE qmake_error) if(qmake_error) message(FATAL_ERROR "Error when executing ${QT_QMAKE_EXECUTABLE} --version\n${qmake_error}") endif() string(REGEX REPLACE ".*Qt version ([0-9.]+) .*" "\\1" MY_QT_VERSION ${MY_QT_VERSION}) endif() # # Project specific properties # if(NOT CTEST_BUILD_NAME) if(MITK_USE_QT) set(CTEST_BUILD_NAME "${MY_OPERATING_SYSTEM} ${MY_COMPILER} Qt${MY_QT_VERSION} ${CTEST_BUILD_CONFIGURATION}") else() set(CTEST_BUILD_NAME "${MY_OPERATING_SYSTEM} ${MY_COMPILER} ${CTEST_BUILD_CONFIGURATION}") endif() + set(CTEST_BUILD_NAME "${CTEST_BUILD_NAME}${CTEST_BUILD_NAME_SUFFIX}") endif() set(PROJECT_BUILD_DIR "MITK-build") set(CTEST_PATH "$ENV{PATH}") if(WIN32) set(ANN_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ANN-build/${CTEST_BUILD_CONFIGURATION}") set(CPPUNIT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/CppUnit-build/${CTEST_BUILD_CONFIGURATION}") set(GLUT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GLUT-build/${CTEST_BUILD_CONFIGURATION}") set(GLEW_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GLEW-build/${CTEST_BUILD_CONFIGURATION}") set(TINYXML_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/tinyxml-build/${CTEST_BUILD_CONFIGURATION}") set(QWT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Qwt-build/${CTEST_BUILD_CONFIGURATION}") set(VTK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/VTK-build/bin/${CTEST_BUILD_CONFIGURATION}") set(ACVD_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ACVD-build/bin/${CTEST_BUILD_CONFIGURATION}") set(ITK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ITK-build/bin/${CTEST_BUILD_CONFIGURATION}") set(BOOST_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Boost-install/lib") set(GDCM_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GDCM-build/bin/${CTEST_BUILD_CONFIGURATION}") set(DCMTK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/DCMTK-install/bin/${CTEST_BUILD_CONFIGURATION}") set(OPENCV_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/OpenCV-build/bin/${CTEST_BUILD_CONFIGURATION}") set(POCO_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Poco-install/lib") set(SOFA_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/SOFA-build/bin/${CTEST_BUILD_CONFIGURATION}") set(BLUEBERRY_OSGI_DIR "${CTEST_BINARY_DIRECTORY}/MITK-build/bin/BlueBerry/org.blueberry.osgi/bin/${CTEST_BUILD_CONFIGURATION}") set(CTEST_PATH "${CTEST_PATH};${CPPUNIT_BINARY_DIR};${QT_BINARY_DIR};${VTK_BINARY_DIR};${ANN_BINARY_DIR};${GLUT_BINARY_DIR};${GLEW_BINARY_DIR};${TINYXML_BINARY_DIR};${QWT_BINARY_DIR};${ACVD_BINARY_DIR};${ITK_BINARY_DIR};${BOOST_BINARY_DIR};${GDCM_BINARY_DIR};${DCMTK_BINARY_DIR};${OPENCV_BINARY_DIR};${POCO_BINARY_DIR};${SOFA_BINARY_DIR};${BLUEBERRY_OSGI_DIR}") endif() set(ENV{PATH} "${CTEST_PATH}") set(SUPERBUILD_TARGETS "") # If the dashscript doesn't define a GIT_REPOSITORY variable, let's define it here. if(NOT DEFINED GIT_REPOSITORY OR GIT_REPOSITORY STREQUAL "") set(GIT_REPOSITORY "http://git.mitk.org/MITK.git") endif() # # Display build info # message("Site name: ${CTEST_SITE}") message("Build name: ${CTEST_BUILD_NAME}") message("Script Mode: ${SCRIPT_MODE}") message("Coverage: ${WITH_COVERAGE}, MemCheck: ${WITH_MEMCHECK}") # # Set initial cache options # if(${CMAKE_VERSION} VERSION_GREATER "2.8.9") set(CTEST_USE_LAUNCHERS 1) set(ENV{CTEST_USE_LAUNCHERS_DEFAULT} 1) endif() # Remove this if block after all dartclients work if(DEFINED ADDITIONNAL_CMAKECACHE_OPTION) message(WARNING "Rename ADDITIONNAL to ADDITIONAL in your dartlclient script: ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") set(ADDITIONAL_CMAKECACHE_OPTION ${ADDITIONNAL_CMAKECACHE_OPTION}) endif() if(NOT DEFINED MITK_USE_ACVD) set(MITK_USE_ACVD 1) endif() if(NOT DEFINED MITK_USE_Boost) set(MITK_USE_Boost 1) endif() if(NOT DEFINED MITK_USE_OpenCV) set(MITK_USE_OpenCV 1) endif() if(NOT DEFINED MITK_USE_Poco) set(MITK_USE_Poco 1) endif() if(NOT DEFINED MITK_USE_Python) if(WIN32) set(MITK_USE_Python FALSE) else() set(MITK_USE_Python TRUE) endif() endif() if(NOT DEFINED MITK_USE_SOFA) set(MITK_USE_SOFA 1) endif() if(NOT DEFINED MITK_VTK_DEBUG_LEAKS) set(MITK_VTK_DEBUG_LEAKS 1) endif() if(NOT DEFINED MITK_BUILD_ALL_APPS) set(MITK_BUILD_ALL_APPS TRUE) endif() if(NOT DEFINED BLUEBERRY_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS TRUE) endif() if(NOT DEFINED MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS TRUE) endif() if(NOT DEFINED MITK_BUILD_EXAMPLES) set(MITK_BUILD_EXAMPLES TRUE) endif() if(NOT BUILD_DiffusionMiniApps) set(BUILD_DiffusionMiniApps TRUE) endif() set(INITIAL_CMAKECACHE_OPTIONS " BLUEBERRY_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS} MITK_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS} MITK_BUILD_ALL_APPS:BOOL=${MITK_BUILD_ALL_APPS} MITK_BUILD_EXAMPLES:BOOL=${MITK_BUILD_EXAMPLES} SUPERBUILD_EXCLUDE_MITKBUILD_TARGET:BOOL=TRUE MITK_USE_ACVD:BOOL=${MITK_USE_ACVD} MITK_USE_Boost:BOOL=${MITK_USE_Boost} MITK_USE_OpenCV:BOOL=${MITK_USE_OpenCV} MITK_USE_Poco:BOOL=${MITK_USE_Poco} MITK_USE_Python:BOOL=${MITK_USE_Python} MITK_USE_SOFA:BOOL=${MITK_USE_SOFA} MITK_USE_QT:BOOL=${MITK_USE_QT} MITK_VTK_DEBUG_LEAKS:BOOL=${MITK_VTK_DEBUG_LEAKS} ${ADDITIONAL_CMAKECACHE_OPTION} ") if(MITK_USE_QT) set(INITIAL_CMAKECACHE_OPTIONS "${INITIAL_CMAKECACHE_OPTIONS} QT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}") endif() # Write a cache file for populating the MITK initial cache (not the superbuild cache). # This can be used to provide variables which are not passed through the # superbuild process to the MITK configure step) if(MITK_INITIAL_CACHE) set(mitk_cache_file "${CTEST_SCRIPT_DIRECTORY}/mitk_initial_cache.txt") file(WRITE "${mitk_cache_file}" "${MITK_INITIAL_CACHE}") set(INITIAL_CMAKECACHE_OPTIONS "${INITIAL_CMAKECACHE_OPTIONS} MITK_INITIAL_CACHE_FILE:INTERNAL=${mitk_cache_file} ") endif() # # Download and include dashboard driver script # set(url "http://mitk.org/git/?p=MITK.git;a=blob_plain;f=CMake/MITKDashboardDriverScript.cmake;hb=${hb}") set(dest ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}.driver) downloadFile("${url}" "${dest}") include(${dest}) diff --git a/CMake/PackageDepends/MITK_Boost_Config.cmake b/CMake/PackageDepends/MITK_Boost_Config.cmake index 1f2eb572f2..0414b37a77 100644 --- a/CMake/PackageDepends/MITK_Boost_Config.cmake +++ b/CMake/PackageDepends/MITK_Boost_Config.cmake @@ -1,29 +1,29 @@ if(MITK_USE_Boost) if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) if(MITK_USE_Boost_LIBRARIES) if(NOT MITK_USE_SYSTEM_Boost) set(BOOST_INCLUDEDIR ${CMAKE_BINARY_DIR}/../Boost-install/include) set(BOOST_LIBRARYDIR ${CMAKE_BINARY_DIR}/../Boost-install/lib) - set(Boost_ADDITIONAL_VERSIONS 1.55) + set(Boost_ADDITIONAL_VERSIONS 1.56) endif() - find_package(Boost 1.55.0 REQUIRED COMPONENTS ${MITK_USE_Boost_LIBRARIES} QUIET) + find_package(Boost 1.56.0 REQUIRED COMPONENTS ${MITK_USE_Boost_LIBRARIES} QUIET) else() - find_package(Boost 1.55.0 REQUIRED QUIET) + find_package(Boost 1.56.0 REQUIRED QUIET) endif() list(APPEND ALL_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) if(Boost_LIBRARIES) list(APPEND ALL_LIBRARIES ${Boost_LIBRARIES}) link_directories(${Boost_LIBRARY_DIRS}) endif() endif(MITK_USE_Boost) diff --git a/CMake/PackageDepends/MITK_Eigen_Config.cmake b/CMake/PackageDepends/MITK_Eigen_Config.cmake new file mode 100644 index 0000000000..dfd8299dd7 --- /dev/null +++ b/CMake/PackageDepends/MITK_Eigen_Config.cmake @@ -0,0 +1 @@ +list(APPEND ALL_INCLUDE_DIRECTORIES ${Eigen_DIR}/include/eigen3) diff --git a/CMake/QBundleTemplate/documentation/Manual/Manual.dox b/CMake/QBundleTemplate/documentation/Manual/Manual.dox index 7b764026fa..21d82fb187 100755 --- a/CMake/QBundleTemplate/documentation/Manual/Manual.dox +++ b/CMake/QBundleTemplate/documentation/Manual/Manual.dox @@ -1,13 +1,12 @@ /** -\page @PLUGIN_ID@ @PLUGIN_NAME@ +\page @PLUGIN_ID@ The @PLUGIN_NAME@ \imageMacro{icon.png,"Icon of @PLUGIN_NAME@",2.00} -Available sections: - - \ref @PLUGIN_ID@Overview +\tableofcontents -\section @PLUGIN_ID@Overview +\section @PLUGIN_ID@Overview Overview This is the description for the @PLUGIN_NAME@. */ diff --git a/CMake/mitkFunctionCreateModule.cmake b/CMake/mitkFunctionCreateModule.cmake index 6b56cc13c4..3c4df77d3f 100644 --- a/CMake/mitkFunctionCreateModule.cmake +++ b/CMake/mitkFunctionCreateModule.cmake @@ -1,602 +1,612 @@ function(_link_directories_for_packages) set(ALL_LIBRARY_DIRS ) foreach(package ${ARGN}) if(NOT ${package} MATCHES "^(Qt[45].*|ITK|VTK)$") foreach(dir ${MODULES_PACKAGE_DEPENDS_DIRS}) if(EXISTS "${dir}/MITK_${package}_Config.cmake") include("${dir}/MITK_${package}_Config.cmake") break() endif() endforeach() endif() endforeach() if(ALL_LIBRARY_DIRS) list(REMOVE_DUPLICATES ALL_LIBRARY_DIRS) link_directories(${ALL_LIBRARY_DIRS}) endif() endfunction() ################################################################## # # mitk_create_module # #! Creates a module for the automatic module dependency system within MITK. #! Configurations are generated in the moduleConf directory. #! #! USAGE: #! #! \code #! MITK_CREATE_MODULE([] #! [INCLUDE_DIRS ] #! [INTERNAL_INCLUDE_DIRS ] #! [DEPENDS ] #! [PACKAGE_DEPENDS ] #! [TARGET_DEPENDS #! [EXPORT_DEFINE ] #! [QT_MODULE] #! [HEADERS_ONLY] #! [WARNINGS_AS_ERRORS] #! \endcode #! #! The parameter specifies the name of the module which is used #! create a logical target name. The parameter is options in case the #! MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME variable evaluates to TRUE. The #! module name will then be derived from the directory name in which this #! macro is called. #! #! If set, the following variables will be used to validate the module name: #! #! MITK_MODULE_NAME_REGEX_MATCH The module name must match this regular expression. #! MITK_MODULE_NAME_REGEX_NOT_MATCH The module name must not match this regular expression. #! #! If the MITK_MODULE_NAME_PREFIX variable is set, the module name will be prefixed #! with its contents. #! #! A modules source files are specified in a separate CMake file usually #! called files.cmake, located in the module root directory. The #! mitk_create_module() macro evaluates the following CMake variables #! from the files.cmake file: #! #! - CPP_FILES A list of .cpp files #! - H_FILES A list of .h files without a corresponding .cpp file #! - TXX_FILES A list of .txx files #! - RESOURCE_FILES A list of files (resources) which are embedded into the module #! - MOC_H_FILES A list of Qt header files which should be processed by the MOC #! - UI_FILES A list of .ui Qt UI files #! - QRC_FILES A list of .qrc Qt resource files #! - DOX_FILES A list of .dox Doxygen files #! #! List of variables available after the function is called: #! - MODULE_NAME #! - MODULE_TARGET #! - MODULE_IS_ENABLED #! - MODULE_SUBPROJECTS #! - ALL_META_DEPENDENCIES #! #! \param QT_MODULE deprecated. Just use Qt4 or Qt5 in the PACKAGE_DEPENDS argument. #! \param HEADERS_ONLY specify this if the modules just contains header files. ################################################################## function(mitk_create_module) set(_macro_params SUBPROJECTS # list of CDash labels VERSION # module version number, e.g. "1.2.0" INCLUDE_DIRS # exported include dirs (used in mitkMacroCreateModuleConf.cmake) INTERNAL_INCLUDE_DIRS # include dirs internal to this module DEPENDS # list of modules this module depends on DEPENDS_INTERNAL # list of modules this module internally depends on PACKAGE_DEPENDS # list of "packages this module depends on (e.g. Qt, VTK, etc.) TARGET_DEPENDS # list of CMake targets this module should depend on EXPORT_DEFINE # export macro name for public symbols of this module AUTOLOAD_WITH # a module target name identifying the module which will trigger the # automatic loading of this module ADDITIONAL_LIBS # list of addidtional libraries linked to this module FILES_CMAKE # file name of a CMake file setting source list variables # (defaults to files.cmake) GENERATED_CPP # not used (?) DEPRECATED_SINCE # marks this modules as deprecated DESCRIPTION # a description for this module ) set(_macro_options QT_MODULE # the module makes use of Qt4 features and needs moc and ui generated files FORCE_STATIC # force building this module as a static library HEADERS_ONLY # this module is a headers-only library GCC_DEFAULT_VISIBILITY # do not use gcc visibility flags - all symbols will be exported NO_INIT # do not create CppMicroServices initialization code NO_FEATURE_INFO # do not create a feature info by calling add_feature_info() WARNINGS_AS_ERRORS # treat all compiler warnings as errors EXECUTABLE # create an executable; do not use directly, use mitk_create_executable() instead ) MACRO_PARSE_ARGUMENTS(MODULE "${_macro_params}" "${_macro_options}" ${ARGN}) set(MODULE_NAME ${MODULE_DEFAULT_ARGS}) if(NOT MODULE_NAME) if(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME) get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) else() message(SEND_ERROR "The module name must not be empty") endif() endif() + set(_module_type module) + set(_Module_type Module) + if(MODULE_EXECUTABLE) + set(_module_type executable) + set(_Module_type Executable) + endif() + if(MITK_MODULE_NAME_REGEX_MATCH) if(NOT ${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_MATCH}) - message(SEND_ERROR "The module name \"${MODULE_NAME}\" does not match the regular expression \"${MITK_MODULE_NAME_REGEX_MATCH}\".") + message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" does not match the regular expression \"${MITK_MODULE_NAME_REGEX_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_REGEX_NOT_MATCH) if(${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_NOT_MATCH}) - message(SEND_ERROR "The module name \"${MODULE_NAME}\" must not match the regular expression \"${MITK_MODULE_NAME_REGEX_NOT_MATCH}\".") + message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" must not match the regular expression \"${MITK_MODULE_NAME_REGEX_NOT_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_PREFIX AND NOT MODULE_NAME MATCHES "^${MITK_MODULE_NAME_PREFIX}.*$") set(MODULE_NAME "${MITK_MODULE_NAME_PREFIX}${MODULE_NAME}") endif() if(NOT MODULE_FILES_CMAKE) set(MODULE_FILES_CMAKE files.cmake) endif() if(NOT IS_ABSOLUTE ${MODULE_FILES_CMAKE}) set(MODULE_FILES_CMAKE ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_FILES_CMAKE}) endif() if (MODULE_QT_MODULE) - message(WARNING "QT_MODULE keyword is deprecated (in module ${MODULE_NAME}). Please use PACKAGE_DEPENDS Qt4|QtCore and/or PACKAGE_DEPENDS Qt5|Core instead") + message(WARNING "QT_MODULE keyword is deprecated (in ${_module_type} ${MODULE_NAME}). Please use PACKAGE_DEPENDS Qt4|QtCore and/or PACKAGE_DEPENDS Qt5|Core instead") if (NOT "${MODULE_PACKAGE_DEPENDS}" MATCHES "^.*Qt4.*$") list(APPEND MODULE_PACKAGE_DEPENDS Qt4|QtGui) endif() endif() if(MODULE_HEADERS_ONLY) set(MODULE_TARGET ) if(MODULE_AUTOLOAD_WITH) message(SEND_ERROR "A headers only module cannot be auto-loaded") endif() else() set(MODULE_TARGET ${MODULE_NAME}) endif() if(MODULE_DEPRECATED_SINCE) set(MODULE_IS_DEPRECATED 1) else() set(MODULE_IS_DEPRECATED 0) endif() if(NOT MODULE_SUBPROJECTS) if(MITK_DEFAULT_SUBPROJECTS) set(MODULE_SUBPROJECTS ${MITK_DEFAULT_SUBPROJECTS}) endif() endif() # check if the subprojects exist as targets if(MODULE_SUBPROJECTS) foreach(subproject ${MODULE_SUBPROJECTS}) if(NOT TARGET ${subproject}) message(SEND_ERROR "The subproject ${subproject} does not have a corresponding target") endif() endforeach() endif() # assume worst case set(MODULE_IS_ENABLED 0) # first we check if we have an explicit module build list if(MITK_MODULES_TO_BUILD) list(FIND MITK_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX) if(_MOD_INDEX EQUAL -1) set(MODULE_IS_EXCLUDED 1) endif() endif() if(NOT MODULE_IS_EXCLUDED) # first of all we check for the dependencies _mitk_parse_package_args(${MODULE_PACKAGE_DEPENDS}) mitk_check_module_dependencies(MODULES ${MODULE_DEPENDS} PACKAGES ${PACKAGE_NAMES} MISSING_DEPENDENCIES_VAR _MISSING_DEP PACKAGE_DEPENDENCIES_VAR PACKAGE_NAMES) if(_MISSING_DEP) if(MODULE_NO_FEATURE_INFO) - message("Module ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") + message("${_Module_type} ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") endif() set(MODULE_IS_ENABLED 0) else() set(MODULE_IS_ENABLED 1) # now check for every package if it is enabled. This overlaps a bit with # MITK_CHECK_MODULE ... foreach(_package ${PACKAGE_NAMES}) if((DEFINED MITK_USE_${_package}) AND NOT (MITK_USE_${_package})) - message("Module ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.") + message("${_Module_type} ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.") set(MODULE_IS_ENABLED 0) break() endif() endforeach() if(MODULE_IS_ENABLED) # clear variables defined in files.cmake set(RESOURCE_FILES ) set(CPP_FILES ) set(H_FILES ) set(TXX_FILES ) set(DOX_FILES ) set(UI_FILES ) set(MOC_H_FILES ) set(QRC_FILES ) # clear other variables set(Q${KITNAME}_GENERATED_CPP ) set(Q${KITNAME}_GENERATED_MOC_CPP ) set(Q${KITNAME}_GENERATED_QRC_CPP ) set(Q${KITNAME}_GENERATED_UI_CPP ) # check and set-up auto-loading if(MODULE_AUTOLOAD_WITH) if(NOT TARGET "${MODULE_AUTOLOAD_WITH}") message(SEND_ERROR "The module target \"${MODULE_AUTOLOAD_WITH}\" specified as the auto-loading module for \"${MODULE_NAME}\" does not exist") endif() set(_module_autoload_meta_target "${MODULE_AUTOLOAD_WITH}-autoload") # create a meta-target if it does not already exist if(NOT TARGET ${_module_autoload_meta_target}) add_custom_target(${_module_autoload_meta_target}) endif() endif() # Convert relative include dirs to absolute dirs set(_include_dirs . ${MODULE_INCLUDE_DIRS}) set(MODULE_INCLUDE_DIRS) foreach(dir ${_include_dirs}) get_filename_component(_abs_dir ${dir} ABSOLUTE) list(APPEND MODULE_INCLUDE_DIRS ${_abs_dir}) endforeach() list(APPEND MODULE_INCLUDE_DIRS ${MITK_BINARY_DIR} ${MODULES_CONF_DIRS}) # Convert relative internal include dirs to absolute dirs set(_include_dirs ${MODULE_INTERNAL_INCLUDE_DIRS}) set(MODULE_INTERNAL_INCLUDE_DIRS) foreach(dir ${_include_dirs}) get_filename_component(_abs_dir ${dir} ABSOLUTE) list(APPEND MODULE_INTERNAL_INCLUDE_DIRS ${_abs_dir}) endforeach() # Qt generates headers in the binary tree list(APPEND MODULE_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) # Add the module specific include dirs include_directories(${MODULE_INCLUDE_DIRS} ${MODULE_INTERNAL_INCLUDE_DIRS}) if(NOT MODULE_EXECUTABLE) _MITK_CREATE_MODULE_CONF() endif() if(NOT MODULE_EXPORT_DEFINE) set(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT) endif(NOT MODULE_EXPORT_DEFINE) if(MITK_GENERATE_MODULE_DOT) message("MODULEDOTNAME ${MODULE_NAME}") foreach(dep ${MODULE_DEPENDS}) message("MODULEDOT \"${MODULE_NAME}\" -> \"${dep}\" ; ") endforeach(dep) endif(MITK_GENERATE_MODULE_DOT) # ok, now create the module itself include(${MODULE_FILES_CMAKE}) set(module_c_flags ) set(module_c_flags_debug ) set(module_c_flags_release ) set(module_cxx_flags ) set(module_cxx_flags_debug ) set(module_cxx_flags_release ) if(MODULE_GCC_DEFAULT_VISIBILITY) set(use_visibility_flags 0) else() # We only support hidden visibility for gcc for now. Clang 3.0 still has troubles with # correctly marking template declarations and explicit template instantiations as exported. # See http://comments.gmane.org/gmane.comp.compilers.clang.scm/50028 # and http://llvm.org/bugs/show_bug.cgi?id=10113 if(CMAKE_COMPILER_IS_GNUCXX) set(use_visibility_flags 1) else() # set(use_visibility_flags 0) endif() endif() if(CMAKE_COMPILER_IS_GNUCXX) # MinGW does not export all symbols automatically, so no need to set flags. # # With gcc < 4.5, RTTI symbols from classes declared in third-party libraries # which are not "gcc visibility aware" are marked with hidden visibility in # DSOs which include the class declaration and which are compiled with # hidden visibility. This leads to dynamic_cast and exception handling problems. # While this problem could be worked around by sandwiching the include # directives for the third-party headers between "#pragma visibility push/pop" # statements, it is generally safer to just use default visibility with # gcc < 4.5. if(${GCC_VERSION} VERSION_LESS "4.5" OR MINGW) set(use_visibility_flags 0) endif() endif() if(use_visibility_flags) mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility=hidden" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility-inlines-hidden" module_c_flags module_cxx_flags) endif() configure_file(${MITK_SOURCE_DIR}/CMake/moduleExports.h.in ${CMAKE_BINARY_DIR}/${MODULES_CONF_DIRNAME}/${MODULE_NAME}Exports.h @ONLY) if(MODULE_WARNINGS_AS_ERRORS) if(MSVC_VERSION) mitkFunctionCheckCAndCXXCompilerFlags("/WX" module_c_flags module_cxx_flags) else() mitkFunctionCheckCAndCXXCompilerFlags("-Werror" module_c_flags module_cxx_flags) # The flag "c++0x-static-nonintegral-init" has been renamed in newer Clang # versions to "static-member-init", see # http://clang-developers.42468.n3.nabble.com/Wc-0x-static-nonintegral-init-gone-td3999651.html # # Also, older Clang and seemingly all gcc versions do not warn if unknown # "-no-*" flags are used, so CMake will happily append any -Wno-* flag to the # command line. This may get confusing if unrelated compiler errors happen and # the error output then additionally contains errors about unknown flags (which # is not the case if there were no compile errors). # # So instead of using -Wno-* we use -Wno-error=*, which will be properly rejected by # the compiler and if applicable, prints the specific warning as a real warning and # not as an error (although -Werror was given). mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=c++0x-static-nonintegral-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=static-member-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unknown-warning" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=gnu" module_c_flags module_cxx_flags) # VNL headers throw a lot of these, not fixable for us at least in ITK 3 mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unused-parameter" module_c_flags module_cxx_flags) # Some DICOM header file in ITK mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=cast-align" module_c_flags module_cxx_flags) endif() endif(MODULE_WARNINGS_AS_ERRORS) if(MODULE_FORCE_STATIC) set(_STATIC STATIC) else() set(_STATIC ) endif(MODULE_FORCE_STATIC) # create a meta-target for auto-loaded modules add_custom_target(${MODULE_NAME}-autoload) if(NOT MODULE_HEADERS_ONLY) - if(NOT MODULE_NO_INIT) + if(NOT MODULE_NO_INIT OR RESOURCE_FILES) find_package(CppMicroServices QUIET NO_MODULE REQUIRED) - if(MODULE_EXECUTABLE) - usFunctionGenerateExecutableInit(CPP_FILES - IDENTIFIER ${MODULE_TARGET} - ) - else() - usFunctionGenerateModuleInit(CPP_FILES - NAME ${MODULE_NAME} - LIBRARY_NAME ${MODULE_TARGET} - ) - endif() + endif() + if(NOT MODULE_NO_INIT) + usFunctionGenerateModuleInit(CPP_FILES) endif() + set(binary_res_files ) + set(source_res_files ) if(RESOURCE_FILES) set(res_dir Resources) - set(binary_res_files ) - set(source_res_files ) foreach(res_file ${RESOURCE_FILES}) if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}/${res_file}) list(APPEND binary_res_files "${res_file}") else() list(APPEND source_res_files "${res_file}") endif() endforeach() - set(res_macro_args ) - if(binary_res_files) - list(APPEND res_macro_args ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${res_dir} - FILES ${binary_res_files}) - endif() + # Add a source level dependencies on resource files if(source_res_files) - list(APPEND res_macro_args ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir} - FILES ${source_res_files}) + list(APPEND CPP_FILES ${MODULE_TARGET}_resources.cpp) + endif() + if(binary_res_files) + list(APPEND CPP_FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}_binary_resources.cpp) endif() - - usFunctionEmbedResources(CPP_FILES - LIBRARY_NAME ${MODULE_TARGET} - ${res_macro_args}) endif() - endif() # Qt 4 case if(MITK_USE_Qt4) if(UI_FILES) qt4_wrap_ui(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES}) endif() if(MOC_H_FILES) qt4_wrap_cpp(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) endif() if(QRC_FILES) qt4_add_resources(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES}) endif() endif() # all the same for Qt 5 if(MITK_USE_Qt5) if(UI_FILES) qt5_wrap_ui(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES}) endif() if(MOC_H_FILES) qt5_wrap_cpp(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) endif() if(QRC_FILES) qt5_add_resources(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES}) endif() endif() set(Q${KITNAME}_GENERATED_CPP ${Q${KITNAME}_GENERATED_CPP} ${Q${KITNAME}_GENERATED_UI_CPP} ${Q${KITNAME}_GENERATED_MOC_CPP} ${Q${KITNAME}_GENERATED_QRC_CPP}) ORGANIZE_SOURCES(SOURCE ${CPP_FILES} HEADER ${H_FILES} TXX ${TXX_FILES} DOC ${DOX_FILES} UI ${UI_FILES} QRC ${QRC_FILES} MOC ${Q${KITNAME}_GENERATED_MOC_CPP} GEN_QRC ${Q${KITNAME}_GENERATED_QRC_CPP} GEN_UI ${Q${KITNAME}_GENERATED_UI_CPP}) set(coverage_sources ${CPP_FILES} ${H_FILES} ${GLOBBED__H_FILES} ${CORRESPONDING__H_FILES} ${TXX_FILES} ${TOOL_CPPS} ${TOOL_GUI_CPPS}) if(MODULE_SUBPROJECTS) set_property(SOURCE ${coverage_sources} APPEND PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() if(NOT MODULE_HEADERS_ONLY) # We have to include the MITK__Config.cmake files here because # some external packages do not provide exported targets with an # absolute path to link to. So we need to add link directories *before* # add_library() or add_executable() is called. So far, this is needed only # for GDCM and ACVD. _link_directories_for_packages(${PACKAGE_NAMES}) # Apply properties to the module target. # We cannot use set_target_properties like below since there is no way to # differentiate C/C++ and Releas/Debug flags using target properties. # See http://www.cmake.org/Bug/view.php?id=6493 #set_target_properties(${MODULE_TARGET} PROPERTIES # COMPILE_FLAGS "${module_compile_flags}") # # Strangely, we need to set the variables below in the parent scope # (outside of the function) to be picked up by the target. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${module_c_flags}" PARENT_SCOPE) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${module_c_flags_debug}" PARENT_SCOPE) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${module_c_flags_release}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${module_cxx_flags}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${module_cxx_flags_debug}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${module_cxx_flags_release}" PARENT_SCOPE) if(MODULE_EXECUTABLE) add_executable(${MODULE_TARGET} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) + set(_us_module_name main) else() add_library(${MODULE_TARGET} ${_STATIC} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) + set(_us_module_name ${MODULE_TARGET}) endif() + set_property(TARGET ${MODULE_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${_us_module_name}) + set_property(TARGET ${MODULE_TARGET} PROPERTY US_MODULE_NAME ${_us_module_name}) if(MODULE_TARGET_DEPENDS) add_dependencies(${MODULE_TARGET} ${MODULE_TARGET_DEPENDS}) endif() if(MODULE_SUBPROJECTS) set_property(TARGET ${MODULE_TARGET} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) foreach(subproject ${MODULE_SUBPROJECTS}) add_dependencies(${subproject} ${MODULE_TARGET}) endforeach() endif() set(DEPENDS "${MODULE_DEPENDS}") if(NOT MODULE_NO_INIT) # Add a CppMicroServices dependency implicitly, since it is # needed for the generated "module initialization" code. set(DEPENDS "CppMicroServices;${DEPENDS}") endif() if(DEPENDS OR MODULE_PACKAGE_DEPENDS) mitk_use_modules(TARGET ${MODULE_TARGET} MODULES ${DEPENDS} PACKAGES ${MODULE_PACKAGE_DEPENDS} ) endif() if(MINGW) target_link_libraries(${MODULE_TARGET} ssp) # add stack smash protection lib endif() # Add additional library search directories to a global property which # can be evaluated by other CMake macros, e.g. our install scripts. if(MODULE_ADDITIONAL_LIBS) target_link_libraries(${MODULE_TARGET} ${MODULE_ADDITIONAL_LIBS}) get_property(_mitk_additional_library_search_paths GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) foreach(_lib_filepath ${MODULE_ADDITIONAL_LIBS}) get_filename_component(_search_path "${_lib_filepath}" PATH) if(_search_path) list(APPEND _mitk_additional_library_search_paths "${_search_path}") endif() endforeach() if(_mitk_additional_library_search_paths) list(REMOVE_DUPLICATES _mitk_additional_library_search_paths) set_property(GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS ${_mitk_additional_library_search_paths}) endif() endif() # add the target name to a global property which is used in the top-level # CMakeLists.txt file to export the target set_property(GLOBAL APPEND PROPERTY MITK_MODULE_TARGETS ${MODULE_TARGET}) if(MODULE_AUTOLOAD_WITH) # for auto-loaded modules, adapt the output directory add_dependencies(${_module_autoload_meta_target} ${MODULE_TARGET}) if(WIN32) set(_module_output_prop RUNTIME_OUTPUT_DIRECTORY) else() set(_module_output_prop LIBRARY_OUTPUT_DIRECTORY) endif() set(_module_output_dir ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) get_target_property(_module_is_imported ${MODULE_AUTOLOAD_WITH} IMPORTED) if(NOT _module_is_imported) # if the auto-loading module is not imported, get its location # and put the auto-load module relative to it. get_target_property(_module_output_dir ${MODULE_AUTOLOAD_WITH} ${_module_output_prop}) set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${_module_output_dir}/${MODULE_AUTOLOAD_WITH}) else() set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) endif() set_target_properties(${MODULE_TARGET} PROPERTIES MITK_AUTOLOAD_DIRECTORY ${MODULE_AUTOLOAD_WITH}) # add the auto-load module name as a property set_property(TARGET ${MODULE_AUTOLOAD_WITH} APPEND PROPERTY MITK_AUTOLOAD_TARGETS ${MODULE_TARGET}) endif() + + if(binary_res_files) + usFunctionAddResources(TARGET ${MODULE_TARGET} + SOURCE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET}_binary_resources.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${res_dir} + FILES ${binary_res_files}) + endif() + if(source_res_files) + usFunctionAddResources(TARGET ${MODULE_TARGET} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir} + FILES ${source_res_files}) + endif() + endif() endif() endif() endif() if(NOT MODULE_IS_ENABLED AND NOT MODULE_EXECUTABLE) _MITK_CREATE_MODULE_CONF() endif() if(_MISSING_DEP) if(MODULE_DESCRIPTION) set(MODULE_DESCRIPTION "${MODULE_DESCRIPTION} (missing dependencies: ${_MISSING_DEP})") else() set(MODULE_DESCRIPTION "(missing dependencies: ${_MISSING_DEP})") endif() endif() if(NOT MODULE_NO_FEATURE_INFO) add_feature_info(${MODULE_NAME} MODULE_IS_ENABLED "${MODULE_DESCRIPTION}") endif() set(MODULE_NAME ${MODULE_NAME} PARENT_SCOPE) set(MODULE_TARGET ${MODULE_TARGET} PARENT_SCOPE) set(MODULE_IS_ENABLED ${MODULE_IS_ENABLED} PARENT_SCOPE) set(MODULE_SUBPROJECTS ${MODULE_SUBPROJECTS} PARENT_SCOPE) set(ALL_META_DEPENDENCIES ${ALL_META_DEPENDENCIES} PARENT_SCOPE) endfunction() diff --git a/CMake/mitkMacroCreateCTKPlugin.cmake b/CMake/mitkMacroCreateCTKPlugin.cmake index b8c4ca5ff9..3067118d3a 100644 --- a/CMake/mitkMacroCreateCTKPlugin.cmake +++ b/CMake/mitkMacroCreateCTKPlugin.cmake @@ -1,87 +1,90 @@ macro(MACRO_CREATE_MITK_CTK_PLUGIN) MACRO_PARSE_ARGUMENTS(_PLUGIN "EXPORT_DIRECTIVE;EXPORTED_INCLUDE_SUFFIXES;MODULE_DEPENDENCIES;MODULE_DEPENDS;PACKAGE_DEPENDS;SUBPROJECTS" "TEST_PLUGIN;NO_INSTALL" ${ARGN}) mitk_check_module_dependencies(MODULES MitkCore ${_PLUGIN_MODULE_DEPENDENCIES} ${_PLUGIN_MODULE_DEPENDS} PACKAGES ${_PLUGIN_PACKAGE_DEPENDS} MISSING_DEPENDENCIES_VAR _missing_deps MODULE_DEPENDENCIES_VAR _module_deps PACKAGE_DEPENDENCIES_VAR _package_deps) if(_PLUGIN_MODULE_DEPENDENCIES) message(WARNING "The MODULE_DEPENDENCIES argument is deprecated since 2014.03. Please use MODULE_DEPENDS instead.") endif() if(NOT _missing_deps) if(_PLUGIN_TEST_PLUGIN) set(is_test_plugin "TEST_PLUGIN") set(_PLUGIN_NO_INSTALL 1) else() set(is_test_plugin) endif() if(_PLUGIN_NO_INSTALL) set(plugin_no_install "NO_INSTALL") else() set(plugin_no_install) endif() _link_directories_for_packages(${_package_deps}) MACRO_CREATE_CTK_PLUGIN(EXPORT_DIRECTIVE ${_PLUGIN_EXPORT_DIRECTIVE} EXPORTED_INCLUDE_SUFFIXES ${_PLUGIN_EXPORTED_INCLUDE_SUFFIXES} DOXYGEN_TAGFILES ${_PLUGIN_DOXYGEN_TAGFILES} MOC_OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED ${is_test_plugin} ${plugin_no_install}) mitk_use_modules(TARGET ${PLUGIN_TARGET} MODULES MitkCore ${_PLUGIN_MODULE_DEPENDENCIES} ${_PLUGIN_MODULE_DEPENDS} PACKAGES ${_PLUGIN_PACKAGE_DEPENDS} ) + set_property(TARGET ${PLUGIN_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${PLUGIN_TARGET}) + set_property(TARGET ${PLUGIN_TARGET} PROPERTY US_MODULE_NAME ${PLUGIN_TARGET}) + if(ALL_META_DEPENDENCIES) add_dependencies(${PLUGIN_TARGET} ${ALL_META_DEPENDENCIES}) endif() if(MITK_DEFAULT_SUBPROJECTS AND NOT MY_SUBPROJECTS) set(MY_SUBPROJECTS ${MITK_DEFAULT_SUBPROJECTS}) endif() if(MY_SUBPROJECTS) set_property(TARGET ${PLUGIN_TARGET} PROPERTY LABELS ${MY_SUBPROJECTS}) foreach(subproject ${MY_SUBPROJECTS}) add_dependencies(${subproject} ${PLUGIN_TARGET}) endforeach() endif() #------------------------------------------------------------# #------------------ Installer support -----------------------# if(NOT _PLUGIN_NO_INSTALL) set(_autoload_targets ) foreach(_dependency ${_module_deps}) get_target_property(_dep_autoloads ${_dependency} MITK_AUTOLOAD_TARGETS) if (_dep_autoloads) list(APPEND _autoload_targets ${_dep_autoloads}) endif() endforeach() # The MITK_AUTOLOAD_TARGETS property is used in the mitkFunctionInstallAutoLoadModules # macro which expects a list of plug-in targets. if (_autoload_targets) list(REMOVE_DUPLICATES _autoload_targets) set_target_properties(${PLUGIN_TARGET} PROPERTIES MITK_AUTOLOAD_TARGETS "${_autoload_targets}") endif() endif() else() if(NOT MITK_BUILD_ALL_PLUGINS) message(SEND_ERROR "${PROJECT_NAME} is missing requirements and won't be built. Missing: ${_missing_deps}") else() message(STATUS "${PROJECT_NAME} is missing requirements and won't be built. Missing: ${_missing_deps}") endif() endif() endmacro() diff --git a/CMake/mitkMacroCreateModuleTests.cmake b/CMake/mitkMacroCreateModuleTests.cmake index 787d53862d..f61a702b39 100644 --- a/CMake/mitkMacroCreateModuleTests.cmake +++ b/CMake/mitkMacroCreateModuleTests.cmake @@ -1,147 +1,101 @@ # # Create tests and testdriver for this module # # Usage: MITK_CREATE_MODULE_TESTS( [EXTRA_DRIVER_INIT init_code] ) # # EXTRA_DRIVER_INIT is inserted as c++ code in the testdriver and will be executed before each test # macro(MITK_CREATE_MODULE_TESTS) MACRO_PARSE_ARGUMENTS(MODULE_TEST "EXTRA_DRIVER_INIT;EXTRA_DRIVER_INCLUDE;EXTRA_DEPENDS" "US_MODULE" ${ARGN}) if(BUILD_TESTING AND MODULE_IS_ENABLED) - set(OLD_MOC_H_FILES ${MOC_H_FILES}) - set(MOC_H_FILES) include(files.cmake) include_directories(.) - if(DEFINED MOC_H_FILES) - QT4_WRAP_CPP(MODULE_TEST_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) - endif(DEFINED MOC_H_FILES) - - mitk_check_module_dependencies(MODULES ${MODULE_NAME} MitkTestingHelper ${MODULE_TEST_EXTRA_DEPENDS} - PACKAGE_DEPENDENCIES_VAR package_deps) - _link_directories_for_packages(${package_deps}) - set(TESTDRIVER ${MODULE_NAME}TestDriver) - set(MODULE_TEST_EXTRA_DRIVER_INIT "${MODULE_TEST_EXTRA_DRIVER_INIT}") - - # Write a header file containing include directives and custom code - # for the test driver. - set(TESTDRIVER_EXTRA_INCLUDES ) - list(APPEND MODULE_TEST_EXTRA_DRIVER_INCLUDE "mitkLog.h") - list(REMOVE_DUPLICATES MODULE_TEST_EXTRA_DRIVER_INCLUDE) - foreach(_include ${MODULE_TEST_EXTRA_DRIVER_INCLUDE}) - set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES} -#include <${_include}>") - endforeach() - set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES} -#include -std::vector globalCmdLineArgs;") - set(_extra_include_file ${CMAKE_CURRENT_BINARY_DIR}/${TESTDRIVER}_extras.h) - configure_file(${MITK_CMAKE_DIR}/mitkTestDriverExtraIncludes.h.in ${_extra_include_file}) - set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " -for (int avIndex = 1; avIndex < ac; ++avIndex) globalCmdLineArgs.push_back(av[avIndex]); -mitk::LoggingBackend::Register(); -${MODULE_TEST_EXTRA_DRIVER_INIT};" -) - set(CMAKE_TESTDRIVER_AFTER_TESTMAIN "mitk::LoggingBackend::Unregister();") - - create_test_sourcelist(MODULETEST_SOURCE ${MODULE_NAME}TestDriver.cpp - ${MODULE_TESTS} ${MODULE_IMAGE_TESTS} ${MODULE_SURFACE_TESTS} ${MODULE_CUSTOM_TESTS} - EXTRA_INCLUDE ${_extra_include_file} - ) + set(MODULE_TEST_EXTRA_DRIVER_INIT "${MODULE_TEST_EXTRA_DRIVER_INIT}") + set(_no_init NO_INIT) if(MODULE_TEST_US_MODULE) - set(testdriver_init_file ) - find_package(CppMicroServices QUIET NO_MODULE REQUIRED) - # Create CppMicroServices initialization code - usFunctionGenerateExecutableInit(testdriver_init_file - IDENTIFIER ${TESTDRIVER} - ) - list(APPEND TEST_CPP_FILES ${testdriver_init_file}) + set(_no_init ) endif() - add_executable(${TESTDRIVER} ${MODULETEST_SOURCE} ${MODULE_TEST_GENERATED_MOC_CPP} ${TEST_CPP_FILES}) - mitk_use_modules(TARGET ${TESTDRIVER} - MODULES ${MODULE_NAME} MitkTestingHelper ${MODULE_TEST_EXTRA_DEPENDS} - ) - - if(MODULE_SUBPROJECTS) - foreach(subproject ${MODULE_SUBPROJECTS}) - add_dependencies(${subproject} ${TESTDRIVER}) - endforeach() - endif() + set(MITK_MODULE_NAME_REGEX_MATCH ) + set(MITK_MODULE_NAME_REGEX_NOT_MATCH ) - # Add meta dependencies (e.g. on auto-load modules from depending modules) - if(ALL_META_DEPENDENCIES) - add_dependencies(${TESTDRIVER} ${ALL_META_DEPENDENCIES}) - endif() + set(_testdriver_file_list ${CMAKE_CURRENT_BINARY_DIR}/testdriver_files.cmake) + configure_file(${MITK_CMAKE_DIR}/mitkTestDriverFiles.cmake.in ${_testdriver_file_list} @ONLY) + mitk_create_executable(${TESTDRIVER} + DEPENDS ${MODULE_NAME} ${MODULE_TEST_EXTRA_DEPENDS} MitkTestingHelper + SUBPROJECTS ${MODULE_SUBPROJECTS} + FILES_CMAKE ${_testdriver_file_list} + NO_FEATURE_INFO NO_BATCH_FILE ${_no_init}) # # Now tell CMake which tests should be run. This is done automatically # for all tests in ${KITNAME}_TESTS and ${KITNAME}_IMAGE_TESTS. The IMAGE_TESTS # are run for each image in the TESTIMAGES list. # + include(files.cmake) foreach( test ${MODULE_TESTS} ) get_filename_component(TName ${test} NAME_WE) add_test(${TName} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} ${TName}) # Add labels for CDash subproject support if(MODULE_SUBPROJECTS) set_property(TEST ${TName} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() - endforeach( test ) + endforeach() foreach(image ${MODULE_TESTIMAGES} ${ADDITIONAL_TEST_IMAGES} ) if(EXISTS ${image}) set(IMAGE_FULL_PATH ${image}) - else(EXISTS ${image}) + else() # todo: maybe search other paths as well # yes, please in mitk/Testing/Data, too set(IMAGE_FULL_PATH ${MITK_DATA_DIR}/${image}) - endif(EXISTS ${image}) + endif() if(EXISTS ${IMAGE_FULL_PATH}) foreach( test ${MODULE_IMAGE_TESTS} ) get_filename_component(TName ${test} NAME_WE) get_filename_component(ImageName ${IMAGE_FULL_PATH} NAME) add_test(${TName}_${ImageName} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} ${TName} ${IMAGE_FULL_PATH}) # Add labels for CDash subproject support if(MODULE_SUBPROJECTS) set_property(TEST ${TName}_${ImageName} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() - endforeach( test ) - else(EXISTS ${IMAGE_FULL_PATH}) + endforeach() + else() message("!!!!! No such file: ${IMAGE_FULL_PATH} !!!!!") - endif(EXISTS ${IMAGE_FULL_PATH}) - endforeach( image ) + endif() + endforeach() foreach(surface ${MODULE_TESTSURFACES} ${ADDITIONAL_TEST_SURFACES} ) if(EXISTS ${surface}) set(SURFACE_FULL_PATH ${surface}) - else(EXISTS ${surface}) + else() # todo: maybe search other paths as well # yes, please in mitk/Testing/Data, too set(SURFACE_FULL_PATH ${MITK_DATA_DIR}/${surface}) - endif(EXISTS ${surface}) + endif() if(EXISTS ${SURFACE_FULL_PATH}) foreach( test ${MODULE_SURFACE_TESTS} ) get_filename_component(TName ${test} NAME_WE) get_filename_component(SurfaceName ${SURFACE_FULL_PATH} NAME) add_test(${TName}_${SurfaceName} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} ${TName} ${SURFACE_FULL_PATH}) # Add labels for CDash subproject support if(MODULE_SUBPROJECTS) set_property(TEST ${TName}_${SurfaceName} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() - endforeach( test ) - else(EXISTS ${SURFACE_FULL_PATH}) + endforeach() + else() message("!!!!! No such surface file: ${SURFACE_FULL_PATH} !!!!!") - endif(EXISTS ${SURFACE_FULL_PATH}) - endforeach( surface ) + endif() + endforeach() - set(MOC_H_FILES ${OLD_MOC_H_FILES}) - endif(BUILD_TESTING AND MODULE_IS_ENABLED) + endif() -endmacro(MITK_CREATE_MODULE_TESTS) +endmacro() diff --git a/CMake/mitkTestDriverFiles.cmake.in b/CMake/mitkTestDriverFiles.cmake.in new file mode 100644 index 0000000000..d1154ef6ad --- /dev/null +++ b/CMake/mitkTestDriverFiles.cmake.in @@ -0,0 +1,47 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/files.cmake) + include(${CMAKE_CURRENT_SOURCE_DIR}/files.cmake) +endif() + +# Write a header file containing include directives and custom code +# for the test driver. +set(TESTDRIVER_EXTRA_INCLUDES ) +list(APPEND MODULE_TEST_EXTRA_DRIVER_INCLUDE "mitkLog.h") +list(REMOVE_DUPLICATES MODULE_TEST_EXTRA_DRIVER_INCLUDE) + +foreach(_include ${MODULE_TEST_EXTRA_DRIVER_INCLUDE}) + set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES} +#include <${_include}>") +endforeach() + +set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES} +#include +std::vector globalCmdLineArgs;") + +set(_extra_include_file ${CMAKE_CURRENT_BINARY_DIR}/${TESTDRIVER}_extras.h) +configure_file(${MITK_CMAKE_DIR}/mitkTestDriverExtraIncludes.h.in ${_extra_include_file}) + +set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " +for (int avIndex = 1; avIndex < ac; ++avIndex) globalCmdLineArgs.push_back(av[avIndex]); +mitk::LoggingBackend::Register(); +${MODULE_TEST_EXTRA_DRIVER_INIT};" +) + +set(CMAKE_TESTDRIVER_AFTER_TESTMAIN "mitk::LoggingBackend::Unregister();") + +create_test_sourcelist(_test_cpp_files ${MODULE_NAME}_main.cpp + ${MODULE_TESTS} ${MODULE_IMAGE_TESTS} ${MODULE_SURFACE_TESTS} ${MODULE_CUSTOM_TESTS} + EXTRA_INCLUDE ${_extra_include_file} +) +list(APPEND CPP_FILES ${_test_cpp_files}) + +# Some old CMake scripts use TEST_CPP_FILES in their files.cmake +# file of the test driver to add source fiels to the executable +# (they should just use CPP_FILES like in any other files.cmake +# file instead). +if(TEST_CPP_FILES) + list(APPEND CPP_FILES ${TEST_CPP_FILES}) +endif() + +if(MODULE_RESOURCE_FILES) + list(APPEND RESOURCE_FILES ${MODULE_RESOURCE_FILES}) +endif() \ No newline at end of file diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake index 472d69ca3d..6b609f1d7d 100644 --- a/CMakeExternals/Boost.cmake +++ b/CMakeExternals/Boost.cmake @@ -1,109 +1,109 @@ #----------------------------------------------------------------------------- # Boost #----------------------------------------------------------------------------- if(MITK_USE_Boost) # Sanity checks if(DEFINED BOOST_ROOT AND NOT EXISTS ${BOOST_ROOT}) message(FATAL_ERROR "BOOST_ROOT variable is defined but corresponds to non-existing directory") endif() string(REPLACE "^^" ";" MITK_USE_Boost_LIBRARIES "${MITK_USE_Boost_LIBRARIES}") set(proj Boost) set(proj_DEPENDENCIES ) set(Boost_DEPENDS ${proj}) if(NOT DEFINED BOOST_ROOT AND NOT MITK_USE_SYSTEM_Boost) set(_boost_libs ) set(INSTALL_COMMAND "") if(MITK_USE_Boost_LIBRARIES) # Set the boost root to the libraries install directory set(BOOST_ROOT "${CMAKE_CURRENT_BINARY_DIR}/${proj}-install") # We need binary boost libraries foreach(_boost_lib ${MITK_USE_Boost_LIBRARIES}) set(_boost_libs ${_boost_libs} --with-${_boost_lib}) endforeach() if(WIN32) set(_boost_variant "") set(_shell_extension .bat) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_boost_address_model "address-model=64") else() set(_boost_address_model "address-model=32") endif() if(MSVC) if(MSVC_VERSION EQUAL 1400) set(_boost_toolset "toolset=msvc-8.0") elseif(MSVC_VERSION EQUAL 1500) set(_boost_toolset "toolset=msvc-9.0") elseif(MSVC_VERSION EQUAL 1600) set(_boost_toolset "toolset=msvc-10.0") elseif(MSVC_VERSION EQUAL 1700) set(_boost_toolset "toolset=msvc-11.0") endif() endif() else() if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(_boost_variant "variant=debug") else() set(_boost_variant "variant=release") endif() set(_shell_extension .sh) endif() if(APPLE) set(APPLE_CMAKE_SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/${proj}-cmake/ChangeBoostLibsInstallNameForMac.cmake) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeExternals/ChangeBoostLibsInstallNameForMac.cmake.in ${APPLE_CMAKE_SCRIPT} @ONLY) set(INSTALL_COMMAND ${CMAKE_COMMAND} -P ${APPLE_CMAKE_SCRIPT}) # Set OSX_SYSROOT set (APPLE_SYSROOT_FLAG) if (NOT ${CMAKE_OSX_SYSROOT} STREQUAL "") set (APPLE_SYSROOT_FLAG --sysroot=${CMAKE_OSX_SYSROOT}) endif() # Set the boost build command for apple set(_boost_build_cmd ${CMAKE_CURRENT_BINARY_DIR}/${proj}-src/bjam ${APPLE_SYSROOT_FLAG} --builddir=${CMAKE_CURRENT_BINARY_DIR}/${proj}-build --prefix=${CMAKE_CURRENT_BINARY_DIR}/${proj}-install ${_boost_toolset} ${_boost_address_model} ${_boost_variant} ${_boost_libs} link=shared,static threading=multi runtime-link=shared -q install) else() set(_boost_build_cmd ${CMAKE_CURRENT_BINARY_DIR}/${proj}-src/bjam --build-dir=${CMAKE_CURRENT_BINARY_DIR}/${proj}-build --prefix=${CMAKE_CURRENT_BINARY_DIR}/${proj}-install ${_boost_toolset} ${_boost_address_model} ${_boost_variant} ${_boost_libs} link=shared,static threading=multi runtime-link=shared -q install) endif() set(_boost_cfg_cmd ${CMAKE_CURRENT_BINARY_DIR}/${proj}-src/bootstrap${_shell_extension}) else() # If no libraries are specified set the boost root to the boost src directory set(BOOST_ROOT "${CMAKE_CURRENT_BINARY_DIR}/${proj}-src") set(_boost_cfg_cmd ) set(_boost_build_cmd ) endif() ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src # Boost needs in-source builds BINARY_DIR ${proj}-src PREFIX ${proj}-cmake - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_55_0.tar.bz2 - URL_MD5 d6eef4b4cacb2183f2bf265a5a03a354 + URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_56_0.tar.bz2 + URL_MD5 a744cf167b05d72335f27c88115f211d INSTALL_DIR ${proj}-install CONFIGURE_COMMAND "${_boost_cfg_cmd}" BUILD_COMMAND "${_boost_build_cmd}" INSTALL_COMMAND "${INSTALL_COMMAND}" DEPENDS ${proj_DEPENDENCIES} ) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/Eigen.cmake b/CMakeExternals/Eigen.cmake new file mode 100644 index 0000000000..6f50d23e0f --- /dev/null +++ b/CMakeExternals/Eigen.cmake @@ -0,0 +1,31 @@ +#----------------------------------------------------------------------------- +# Eigen +#----------------------------------------------------------------------------- + +# Sanity checks +if(DEFINED Eigen_DIR AND NOT EXISTS ${Eigen_DIR}) + message(FATAL_ERROR "Eigen_DIR variable is defined but corresponds to non-existing directory") +endif() + +set(proj Eigen) +set(proj_DEPENDENCIES ) +set(Eigen_DEPENDS ${proj}) + +if(NOT DEFINED Eigen_DIR) + + ExternalProject_Add(${proj} + SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src + BINARY_DIR ${proj}-cmake + URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/Eigen-3.2.2.tar.gz + URL_MD5 2809c6a623ef48e7ab3d6a2630b94a29 + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${proj}-install + ) + + set(Eigen_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-install) + +else() + + mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") + +endif() diff --git a/CMakeExternals/MITKData.cmake b/CMakeExternals/MITKData.cmake index e764edaba2..0eb4565c94 100644 --- a/CMakeExternals/MITKData.cmake +++ b/CMakeExternals/MITKData.cmake @@ -1,36 +1,36 @@ #----------------------------------------------------------------------------- # MITK Data #----------------------------------------------------------------------------- # Sanity checks if(DEFINED MITK_DATA_DIR AND NOT EXISTS ${MITK_DATA_DIR}) message(FATAL_ERROR "MITK_DATA_DIR variable is defined but corresponds to non-existing directory") endif() set(proj MITK-Data) set(proj_DEPENDENCIES) set(MITK-Data_DEPENDS ${proj}) if(BUILD_TESTING) - set(revision_tag acedec01) + set(revision_tag d96c6dab) # ^^^^^^^^ these are just to check correct length of hash part ExternalProject_Add(${proj} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MITK-Data_${revision_tag}.tar.gz UPDATE_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${proj_DEPENDENCIES} ) set(MITK_DATA_DIR ${ep_source_dir}/${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif(BUILD_TESTING) diff --git a/CMakeExternals/PatchVTK.cmake b/CMakeExternals/PatchVTK.cmake new file mode 100644 index 0000000000..4eb6989741 --- /dev/null +++ b/CMakeExternals/PatchVTK.cmake @@ -0,0 +1,5 @@ +set(path "Rendering/OpenGL/vtkXOpenGLRenderWindow.cxx") +file(STRINGS ${path} contents NEWLINE_CONSUME) +string(REPLACE "//#define GLX_GLXEXT_LEGACY" "#define GLX_GLXEXT_LEGACY" contents ${contents}) +set(CONTENTS ${contents}) +configure_file(${TEMPLATE_FILE} ${path} @ONLY) diff --git a/CMakeExternals/SOFA.cmake b/CMakeExternals/SOFA.cmake index ea2caaa3ca..78974ecf1a 100644 --- a/CMakeExternals/SOFA.cmake +++ b/CMakeExternals/SOFA.cmake @@ -1,100 +1,100 @@ #----------------------------------------------------------------------------- # SOFA #----------------------------------------------------------------------------- if(MITK_USE_SOFA) # Sanity checks if(DEFINED SOFA_DIR AND NOT EXISTS ${SOFA_DIR}) message(FATAL_ERROR "SOFA_DIR variable is defined but corresponds to non-existing directory") endif() set(proj SOFA) set(proj_DEPENDENCIES Boost GLEW) set(SOFA_DEPENDS ${proj}) set(preconfigure_cmake_args -DGLEW_DIR:PATH=${GLEW_DIR} -DSOFA-APPLICATION_MODELER:BOOL=OFF -DSOFA-APPLICATION_RUNSOFA:BOOL=OFF -DSOFA-APPLICATION_SOFABATCH:BOOL=OFF -DSOFA-EXTERNAL_BOOST:BOOL=ON -DSOFA-EXTERNAL_BOOST_PATH:PATH=${CMAKE_BINARY_DIR}/Boost-install/lib -DSOFA-EXTERNAL_CSPARSE:BOOL=ON -DSOFA-EXTERNAL_GLEW:BOOL=ON -DSOFA-EXTERNAL_PNG:BOOL=OFF -DSOFA-EXTERNAL_ZLIB:BOOL=OFF -DSOFA-LIB_COMPONENT_SPARSE_SOLVER:BOOL=ON -DSOFA-LIB_GUI_GLUT:BOOL=OFF -DSOFA-LIB_GUI_QT:BOOL=OFF -DSOFA-LIB_GUI_QTVIEWER:BOOL=OFF -DSOFA-TUTORIAL_CHAIN_HYBRID:BOOL=OFF -DSOFA-TUTORIAL_COMPOSITE_OBJECT:BOOL=OFF -DSOFA-TUTORIAL_MIXED_PENDULUM:BOOL=OFF -DSOFA-TUTORIAL_ONE_PARTICLE:BOOL=OFF -DSOFA-TUTORIAL_ONE_TETRAHEDRON:BOOL=OFF ) if(NOT APPLE) list(APPEND proj_DEPENDENCIES GLUT) list(APPEND preconfigure_cmake_args -DSOFA-EXTERNAL_FREEGLUT:BOOL=ON -DGLUT_DIR:PATH=${GLUT_DIR} ) endif() if(NOT MITK_USE_SYSTEM_Boost) list(APPEND preconfigure_cmake_args -DBoost_NO_SYSTEM_PATHS:BOOL=ON -DBOOST_INCLUDEDIR:PATH=${CMAKE_BINARY_DIR}/Boost-install/include -DBOOST_LIBRARYDIR:PATH=${CMAKE_BINARY_DIR}/Boost-install/lib - -DBoost_ADDITIONAL_VERSIONS:STRING=1.55 + -DBoost_ADDITIONAL_VERSIONS:STRING=1.56 ) endif() if(MITK_USE_SOFA_PLUGINS_DIR) list(APPEND preconfigure_cmake_args -DSOFA_APPLICATIONS_PLUGINS_DIR:PATH=${MITK_USE_SOFA_PLUGINS_DIR} ) foreach(plugin ${MITK_USE_SOFA_PLUGINS}) string(TOUPPER ${plugin} plugin) list(APPEND preconfigure_cmake_args -DSOFA-PLUGIN_${plugin}:BOOL=ON ) endforeach() endif() set(rev "10669") set(SOFA_PATCH_COMMAND ${CMAKE_COMMAND} -DTEMPLATE_FILE:FILEPATH=${MITK_SOURCE_DIR}/CMakeExternals/EmptyFileForPatching.dummy -P ${MITK_SOURCE_DIR}/CMakeExternals/PatchSOFA-rev${rev}.cmake) set(SOFA_PRECONFIGURE_COMMAND ${CMAKE_COMMAND} -G${gen} ${ep_common_args} ${preconfigure_cmake_args} ${boost_cmake_args} ${CMAKE_BINARY_DIR}/${proj}-src) if(NOT DEFINED SOFA_DIR) ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/SOFA-rev${rev}.tar.gz URL_MD5 d01a194f54b933f4cdfdfc75b2f81a2f PATCH_COMMAND ${SOFA_PATCH_COMMAND} INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Add_Step(${proj} preconfigure COMMAND ${SOFA_PRECONFIGURE_COMMAND} WORKING_DIRECTORY ${proj}-build DEPENDEES patch DEPENDERS configure LOG 1 ) set(SOFA_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-build) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/VTK.cmake b/CMakeExternals/VTK.cmake index 9679cdefd5..cd2ae6a8d9 100644 --- a/CMakeExternals/VTK.cmake +++ b/CMakeExternals/VTK.cmake @@ -1,111 +1,114 @@ #----------------------------------------------------------------------------- # VTK #----------------------------------------------------------------------------- if(WIN32) option(VTK_USE_SYSTEM_FREETYPE OFF) else(WIN32) option(VTK_USE_SYSTEM_FREETYPE ON) endif(WIN32) # Sanity checks if(DEFINED VTK_DIR AND NOT EXISTS ${VTK_DIR}) message(FATAL_ERROR "VTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj VTK) set(proj_DEPENDENCIES ) set(VTK_DEPENDS ${proj}) + set(VTK_PATCH_COMMAND ${CMAKE_COMMAND} -DTEMPLATE_FILE:FILEPATH=${MITK_SOURCE_DIR}/CMakeExternals/EmptyFileForPatching.dummy -P ${MITK_SOURCE_DIR}/CMakeExternals/PatchVTK.cmake) + if(NOT DEFINED VTK_DIR) set(additional_cmake_args ) if(MINGW) set(additional_cmake_args -DCMAKE_USE_WIN32_THREADS:BOOL=ON -DCMAKE_USE_PTHREADS:BOOL=OFF -DVTK_USE_VIDEO4WINDOWS:BOOL=OFF # no header files provided by MinGW ) endif() if(WIN32) # see http://bugs.mitk.org/show_bug.cgi?id=17858 list(APPEND additional_cmake_args -DVTK_DO_NOT_DEFINE_OSTREAM_SLL:BOOL=ON -DVTK_DO_NOT_DEFINE_OSTREAM_ULL:BOOL=ON ) endif() # Optionally enable memory leak checks for any objects derived from vtkObject. This # will force unit tests to fail if they have any of these memory leaks. option(MITK_VTK_DEBUG_LEAKS OFF) mark_as_advanced(MITK_VTK_DEBUG_LEAKS) list(APPEND additional_cmake_args -DVTK_DEBUG_LEAKS:BOOL=${MITK_VTK_DEBUG_LEAKS} ) if(MITK_USE_Python) if(NOT MITK_USE_SYSTEM_PYTHON) list(APPEND proj_DEPENDENCIES Python) endif() list(APPEND additional_cmake_args -DVTK_WRAP_PYTHON:BOOL=ON -DVTK_USE_TK:BOOL=OFF -DVTK_WINDOWS_PYTHON_DEBUGGABLE:BOOL=OFF -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE} -DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR} -DPYTHON_INCLUDE_DIR2:PATH=${PYTHON_INCLUDE_DIR2} -DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY} ) else() list(APPEND additional_cmake_args -DVTK_WRAP_PYTHON:BOOL=OFF -DVTK_WINDOWS_PYTHON_DEBUGGABLE:BOOL=OFF ) endif() if(MITK_USE_QT) list(APPEND additional_cmake_args -DVTK_QT_VERSION:STRING=${DESIRED_QT_VERSION} -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} -DModule_vtkGUISupportQt:BOOL=ON -DModule_vtkGUISupportQtWebkit:BOOL=ON -DModule_vtkGUISupportQtSQL:BOOL=ON -DModule_vtkRenderingQt:BOOL=ON -DVTK_Group_Qt:BOOL=ON ) endif() set(VTK_URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/VTK-6.1.0+74f4888.tar.gz) set(VTK_URL_MD5 1f19dae22c42c032109bd3cf91c4e8c9) ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake URL ${VTK_URL} URL_MD5 ${VTK_URL_MD5} + PATCH_COMMAND ${VTK_PATCH_COMMAND} INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} -DVTK_WRAP_TCL:BOOL=OFF -DVTK_WRAP_PYTHON:BOOL=OFF -DVTK_WRAP_JAVA:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=ON -DVTK_USE_SYSTEM_FREETYPE:BOOL=${VTK_USE_SYSTEM_FREETYPE} -DVTK_LEGACY_REMOVE:BOOL=ON -DModule_vtkTestingRendering:BOOL=ON -DVTK_MAKE_INSTANTIATORS:BOOL=ON ${additional_cmake_args} DEPENDS ${proj_DEPENDENCIES} ) set(VTK_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-build) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index b9ea62a4ef..88f90f7cdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1073 +1,1077 @@ set(DESIRED_QT_VERSION 4 CACHE STRING "Pick a version of Qt to use: 4 or 5") -if(DESIRED_QT_VERSION MATCHES "4") + +if (${CMAKE_GENERATOR} MATCHES "^Visual Studio 12.*$") + message("For MS Visual Studio 12 (2013) the minimum required CMake version is 3") + cmake_minimum_required(VERSION 3.0) +elseif(DESIRED_QT_VERSION MATCHES "4") cmake_minimum_required(VERSION 2.8.9) else() cmake_minimum_required(VERSION 2.8.12) endif() #----------------------------------------------------------------------------- # Include ctest launchers for dashboard in case of makefile generator #----------------------------------------------------------------------------- if(${CMAKE_VERSION} VERSION_GREATER "2.8.9") include(CTestUseLaunchers) endif() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK) endif() #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 43) # _src_dir_length_max - strlen(ITK-src) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # See http://cmake.org/cmake/help/cmake-2-8-docs.html#section_Policies for details #----------------------------------------------------------------------------- set(project_policies CMP0001 # NEW: CMAKE_BACKWARDS_COMPATIBILITY should no longer be used. CMP0002 # NEW: Logical target names must be globally unique. CMP0003 # NEW: Libraries linked via full path no longer produce linker search paths. CMP0004 # NEW: Libraries linked may NOT have leading or trailing whitespace. CMP0005 # NEW: Preprocessor definition values are now escaped automatically. CMP0006 # NEW: Installing MACOSX_BUNDLE targets requires a BUNDLE DESTINATION. CMP0007 # NEW: List command no longer ignores empty elements. CMP0008 # NEW: Libraries linked by full-path must have a valid library file name. CMP0009 # NEW: FILE GLOB_RECURSE calls should not follow symlinks by default. CMP0010 # NEW: Bad variable reference syntax is an error. CMP0011 # NEW: Included scripts do automatic cmake_policy PUSH and POP. CMP0012 # NEW: if() recognizes numbers and boolean constants. CMP0013 # NEW: Duplicate binary directories are not allowed. CMP0014 # NEW: Input directories must have CMakeLists.txt CMP0020 # NEW: Automatically link Qt executables to qtmain target on Windows ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # Update CMake module path #------------------------------------------------------------------------------ set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(mitkMacroEmptyExternalProject) include(mitkFunctionGenerateProjectXml) include(mitkFunctionSuppressWarnings) include(mitkFunctionEnableBuildConfiguration) include(FeatureSummary) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_USE_SUPERBUILD) set(output_dir ${MITK_BINARY_DIR}/bin) if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_BINARY_DIR}/MITK-build/bin) endif() else() if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(output_dir ${MITK_BINARY_DIR}/bin) else() set(output_dir ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) endif() endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${output_dir} CACHE INTERNAL "Single output directory for building all libraries.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) macro(env_option name doc value) set(_value $ENV{${name}}) if("${_value}" STREQUAL "") set(_value ${value}) endif() option(${name} "${doc}" ${_value}) endmacro() # ----------------------------------------- # Qt version related variables env_option(MITK_USE_QT "Use Nokia's Qt library" ON) set(MITK_DESIRED_QT_VERSION ${DESIRED_QT_VERSION}) if(MITK_USE_QT) # find the package at the very beginning, so that QT4_FOUND is available if(DESIRED_QT_VERSION MATCHES 4) set(MITK_QT4_MINIMUM_VERSION 4.7) find_package(Qt4 ${MITK_QT4_MINIMUM_VERSION} REQUIRED) set(MITK_USE_Qt4 TRUE) set(MITK_USE_Qt5 FALSE) endif() if(DESIRED_QT_VERSION MATCHES 5) set(MITK_QT5_MINIMUM_VERSION 5.0.0) set(MITK_USE_Qt4 FALSE) set(MITK_USE_Qt5 TRUE) set(QT5_INSTALL_PREFIX "" CACHE PATH "The install location of Qt5") set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT5_INSTALL_PREFIX}) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) find_package(Qt5Widgets ${MITK_QT5_MINIMUM_VERSION} REQUIRED) endif() else() set(MITK_USE_Qt4 FALSE) set(MITK_USE_Qt5 FALSE) endif() # ----------------------------------------- # MITK_USE_* build variables env_option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) set(MITK_BUILD_TUTORIAL OFF CACHE INTERNAL "Deprecated! Use MITK_BUILD_EXAMPLES instead!") env_option(MITK_BUILD_EXAMPLES "Build the MITK Examples" ${MITK_BUILD_TUTORIAL}) env_option(MITK_USE_ACVD "Use Approximated Centroidal Voronoi Diagrams" OFF) env_option(MITK_USE_CppUnit "Use CppUnit for unit tests" ON) if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() env_option(MITK_USE_GLEW "Use the GLEW library" ON) env_option(MITK_USE_Boost "Use the Boost C++ library" OFF) env_option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) env_option(MITK_USE_CTK "Use CTK in MITK" ON) env_option(MITK_USE_DCMTK "EXPERIMENTAL, superbuild only: Use DCMTK in MITK" ${MITK_USE_CTK}) env_option(MITK_USE_OpenCV "Use Intel's OpenCV library" OFF) env_option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) env_option(MITK_USE_Poco "Use the Poco library" ON) env_option(MITK_USE_SOFA "Use Simulation Open Framework Architecture" OFF) env_option(MITK_USE_Python "Use Python wrapping in MITK" OFF) env_option(MITK_USE_SimpleITK "Use the SimpleITK library" OFF) set(MITK_USE_CableSwig ${MITK_USE_Python}) option(MITK_ENABLE_PIC_READER "Enable support for reading the DKFZ pic file format." ON) set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS Custom Default All) mitkFunctionEnableBuildConfiguration() mark_as_advanced(MITK_BUILD_ALL_APPS MITK_USE_CppUnit MITK_USE_GLEW MITK_USE_CTK MITK_USE_DCMTK MITK_ENABLE_PIC_READER MITK_BUILD_CONFIGURATION ) if(MITK_USE_Python) if(APPLE) message(WARNING "Python wrapping is unsuported on mac OSX!") set(MITK_USE_Python OFF CACHE BOOL "Use Python wrapping in MITK" FORCE) else() option(MITK_USE_SYSTEM_PYTHON "Use the system python runtime" OFF) # SimpleITK is required when python is enabled set(MITK_USE_SimpleITK ON CACHE BOOL "Use the SimpleITK library" FORCE) if(MITK_USE_SYSTEM_PYTHON) FIND_PACKAGE(PythonLibs REQUIRED) FIND_PACKAGE(PythonInterp REQUIRED) else() FIND_PACKAGE(PythonLibs) FIND_PACKAGE(PythonInterp) endif() endif() endif() if(MITK_USE_Boost) option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() if(MITK_USE_CTK AND NOT MITK_USE_DCMTK) message("> Forcing MITK_USE_DCMTK to ON because of MITK_USE_CTK") set(MITK_USE_DCMTK ON CACHE BOOL "Use DCMTK in MITK" FORCE) endif() if(MITK_USE_SOFA) # SOFA requires at least CMake 2.8.8 set(SOFA_CMAKE_VERSION 2.8.8) if(${CMAKE_VERSION} VERSION_LESS ${SOFA_CMAKE_VERSION}) set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE) message(WARNING "Switched off MITK_USE_SOFA\n Minimum required CMake version: ${SOFA_CMAKE_VERSION}\n Installed CMake version: ${CMAKE_VERSION}") endif() # SOFA/ITK combination requires at least MSVC 2010 if(MSVC_VERSION AND MSVC_VERSION LESS 1600) set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE) message(WARNING "Switched off MITK_USE_SOFA\n MSVC versions less than 2010 are not supported.") endif() # SOFA requires boost library if(MITK_USE_SOFA AND NOT MITK_USE_Boost) message("Forcing MITK_USE_Boost to ON because of MITK_USE_SOFA") set(MITK_USE_Boost ON CACHE BOOL "" FORCE) endif() # SOFA requires boost system library list(FIND MITK_USE_Boost_LIBRARIES system _result) if(_result LESS 0) message("Adding 'system' to MITK_USE_Boost_LIBRARIES.") list(APPEND MITK_USE_Boost_LIBRARIES system) endif() # SOFA requires boost thread library list(FIND MITK_USE_Boost_LIBRARIES thread _result) if(_result LESS 0) message("Adding 'thread' to MITK_USE_Boost_LIBRARIES.") list(APPEND MITK_USE_Boost_LIBRARIES thread) endif() # Simulation plugin requires boost chrono library list(FIND MITK_USE_Boost_LIBRARIES chrono _result) if(_result LESS 0) message("Adding 'chrono' to MITK_USE_Boost_LIBRARIES.") list(APPEND MITK_USE_Boost_LIBRARIES chrono) endif() set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "" FORCE) # Allow setting external SOFA plugins directory and SOFA plugins set(MITK_USE_SOFA_PLUGINS_DIR ${MITK_USE_SOFA_PLUGINS_DIR} CACHE PATH "External SOFA plugins directory" FORCE) set(MITK_USE_SOFA_PLUGINS ${MITK_USE_SOFA_PLUGINS} CACHE PATH "List of semicolon-separated plugin names" FORCE) endif() # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() #----------------------------------------------------------------------------- # Project.xml #----------------------------------------------------------------------------- # A list of topologically ordered targets set(CTEST_PROJECT_SUBPROJECTS) if(MITK_USE_BLUEBERRY) list(APPEND CTEST_PROJECT_SUBPROJECTS BlueBerry) endif() list(APPEND CTEST_PROJECT_SUBPROJECTS MITK-Core MITK-CoreUI MITK-IGT MITK-ToF MITK-DTI MITK-Registration MITK-Modules # all modules not contained in a specific subproject MITK-Plugins # all plugins not contained in a specific subproject MITK-Examples Unlabeled # special "subproject" catching all unlabeled targets and tests ) # Configure CTestConfigSubProject.cmake that could be used by CTest scripts configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in ${MITK_BINARY_DIR}/CTestConfigSubProject.cmake) if(CTEST_PROJECT_ADDITIONAL_TARGETS) # those targets will be executed at the end of the ctest driver script # and they also get their own subproject label set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}") else() set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}") endif() # Generate Project.xml file expected by the CTest driver script mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD}) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionGetGccVersion) include(MacroParseArguments) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkFunctionOrganizeSources) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionCreateWindowsBatchScript) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionCompileSnippets) include(mitkFunctionUseModules) include(mitkMacroCreateModuleConf) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCreateModule) include(mitkMacroCreateExecutable) include(mitkMacroCheckModule) include(mitkMacroCreateModuleTests) include(mitkFunctionAddCustomModuleTest) include(mitkMacroUseModule) include(mitkMacroMultiplexPicType) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # ASK THE USER TO SHOW THE CONSOLE WINDOW FOR CoreApp and mitkWorkbench option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(DEFINED MITK_CTEST_SCRIPT_MODE AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") ) set(MITK_FAST_TESTING 1) endif() endif() #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on Mac OSX all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() #----------------------------------------------------------------------------- # Set symbol visibility Flags #----------------------------------------------------------------------------- # MinGW does not export all symbols automatically, so no need to set flags if(CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW) set(VISIBILITY_CXX_FLAGS ) #"-fvisibility=hidden -fvisibility-inlines-hidden") endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${VISIBILITY_CXX_FLAGS} ${COVERAGE_CXX_FLAGS}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -D_WIN32_WINNT=0x0501 -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN -DNOMINMAX") set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} /wd4231") # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo # the following two lines should be removed after ITK-3097 has # been resolved, see also MITK bug 15279 -Wno-unused-local-typedefs -Wno-array-bounds -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) # With older version of gcc supporting the flag -fstack-protector-all, an extra dependency to libssp.so # is introduced. If gcc is smaller than 4.4.0 and the build type is Release let's not include the flag. # Doing so should allow to build package made for distribution using older linux distro. if(${GCC_VERSION} VERSION_GREATER "4.4.0" OR (CMAKE_BUILD_TYPE STREQUAL "Debug" AND ${GCC_VERSION} VERSION_LESS "4.4.0")) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) endif() if(MINGW) # suppress warnings about auto imported symbols set(MITK_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import ${MITK_SHARED_LINKER_FLAGS}") endif() set(MITK_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) enable_testing() include(CTest) mark_as_advanced(TCL_TCLSH DART_ROOT) option(MITK_ENABLE_RENDERING_TESTING OFF "Enable the MITK rendering tests. Requires x-server in Linux.") #Rendering testing does not work for Linux nightlies, thus it is disabled per default #and activated for Mac and Windows. if(WIN32 OR APPLE) set(MITK_ENABLE_RENDERING_TESTING ON) endif() mark_as_advanced( MITK_ENABLE_RENDERING_TESTING ) # Setup file for setting custom ctest vars configure_file( CMake/CTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch( std::exception & excp ) { fprintf(stderr,\"%s\\n\",excp.what()); return EXIT_FAILURE; } catch( ... ) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; } ") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the external project template if(MITK_USE_BLUEBERRY) include(mitkTestProjectTemplate) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Compile Utilities and set-up MITK variables #----------------------------------------------------------------------------- include(mitkSetupVariables) #----------------------------------------------------------------------------- # Cleanup #----------------------------------------------------------------------------- file(GLOB _MODULES_CONF_FILES ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) if(_MODULES_CONF_FILES) file(REMOVE ${_MODULES_CONF_FILES}) endif() add_subdirectory(Utilities) if(MITK_USE_BLUEBERRY) # We need to hack a little bit because MITK applications may need # to enable certain BlueBerry plug-ins. However, these plug-ins # are validated separately from the MITK plug-ins and know nothing # about potential MITK plug-in dependencies of the applications. Hence # we cannot pass the MITK application list to the BlueBerry # ctkMacroSetupPlugins call but need to extract the BlueBerry dependencies # from the applications and set them explicitly. include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled and if target_libraries.cmake exists if((${option_name} OR MITK_BUILD_ALL_APPS) AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") foreach(_target_dep ${target_libraries}) if(_target_dep MATCHES org_blueberry_) string(REPLACE _ . _app_bb_dep ${_target_dep}) # explicitly set the build option for the BlueBerry plug-in set(BLUEBERRY_BUILD_${_app_bb_dep} ON CACHE BOOL "Build the ${_app_bb_dep} plug-in") endif() endforeach() endif() endforeach() set(mbilog_DIR "${mbilog_BINARY_DIR}") if(MITK_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS ON) endif() set(BLUEBERRY_XPDOC_OUTPUT_DIR ${MITK_DOXYGEN_OUTPUT_DIR}/html/extension-points/html/) add_subdirectory(BlueBerry) set(BlueBerry_DIR ${CMAKE_CURRENT_BINARY_DIR}/BlueBerry CACHE PATH "The directory containing a CMake configuration file for BlueBerry" FORCE) include(mitkMacroCreateCTKPlugin) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add custom targets representing CDash subprojects #----------------------------------------------------------------------------- foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled") add_custom_target(${subproject}) endif() endforeach() #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Core) add_subdirectory(Modules) if(MITK_USE_BLUEBERRY) find_package(BlueBerry REQUIRED) set(MITK_DEFAULT_SUBPROJECTS MITK-Plugins) # Plug-in testing (needs some work to be enabled again) if(BUILD_TESTING) include(berryTestingHelpers) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp") get_target_property(_is_macosx_bundle CoreApp MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp.app/Contents/MacOS/CoreApp") endif() set(BLUEBERRY_TEST_APP_ID "org.mitk.qt.coreapplication") endif() include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") set(mitk_plugins_fullpath ) foreach(mitk_plugin ${MITK_EXT_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") set(mitk_apps_fullpath ) foreach(mitk_app ${MITK_APPS}) list(APPEND mitk_apps_fullpath "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${mitk_app}") endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() # 11.3.13, change, muellerm: activate python bundle if python and blueberry is active if( MITK_USE_Python ) set(MITK_BUILD_org.mitk.gui.qt.python ON) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- add_subdirectory(Documentation) #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) # MITK_APPS is set in Applications/AppList.cmake (included somewhere above # if MITK_USE_BLUEBERRY is set to ON). if(MITK_APPS) set(activated_apps_no 0) list(LENGTH MITK_APPS app_count) # Check how many apps have been enabled # If more than one app has been activated, the we use the # default CPack configuration. Otherwise that apps configuration # will be used, if present. foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/Applications/${target_dir}/CPackConfig.cmake") configure_file(${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${target_dir}") endif() endforeach() endif() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() endforeach() get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) set(VISIBILITY_AVAILABLE 0) set(visibility_test_flag "") mitkFunctionCheckCompilerFlags("-fvisibility=hidden" visibility_test_flag) if(visibility_test_flag) # The compiler understands -fvisiblity=hidden (probably gcc >= 4 or Clang) set(VISIBILITY_AVAILABLE 1) endif() configure_file(mitkExportMacros.h.in ${MITK_BINARY_DIR}/mitkExportMacros.h) configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) file(GLOB _MODULES_CONF_FILES RELATIVE ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME} ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) set(MITK_MODULE_NAMES) foreach(_module ${_MODULES_CONF_FILES}) string(REPLACE Config.cmake "" _module_name ${_module}) list(APPEND MITK_MODULE_NAMES ${_module_name}) endforeach() configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) # If we are under Windows, create two batch files which correctly # set up the environment for the application and for Visual Studio if(WIN32) include(mitkFunctionCreateWindowsBatchScript) set(VS_SOLUTION_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln") foreach(VS_BUILD_TYPE debug release) mitkFunctionCreateWindowsBatchScript("${MITK_SOURCE_DIR}/CMake/StartVS.bat.in" ${PROJECT_BINARY_DIR}/StartVS_${VS_BUILD_TYPE}.bat ${VS_BUILD_TYPE}) endforeach() endif(WIN32) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 181090590a..ae64924278 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -1,48 +1,50 @@ #----------------------------------------------------------------------------- # Configure the CppMicroServices build #----------------------------------------------------------------------------- set(US_ENABLE_AUTOLOADING_SUPPORT ON) set(US_ENABLE_THREADING_SUPPORT ON) # Don't create a "doc" target and don't install the documentation files set(US_NO_DOCUMENTATION ON) # Don't use an install component for SDK artifacts set(US_SDK_INSTALL_COMPONENT "") if(BUILD_TESTING) set(US_BUILD_TESTING ON) endif() set(CppMicroServices_DIR_default "${CMAKE_CURRENT_BINARY_DIR}/CppMicroServices") -set(CppMicroServices_DIR ${CppMicroServices_DIR_default} CACHE PATH "Path to the CppMicroServices library") +if(NOT CppMicroServices_DIR) + set(CppMicroServices_DIR ${CppMicroServices_DIR_default} CACHE PATH "Path to the CppMicroServices library" FORCE) +endif() mark_as_advanced(CppMicroServices_DIR) if ("${CppMicroServices_DIR}" STREQUAL "${CppMicroServices_DIR_default}") add_subdirectory(CppMicroServices) endif() # create a custom module conf file for CppMicroServices function(_generate_cppmicroservices_conf) set(MITK_MODULE_NAME_PREFIX "") set(MODULE_IS_ENABLED 1) set(MODULE_NAME CppMicroServices) set(MODULE_TARGET ${MODULE_NAME}) set(MODULE_EXTRA_CMAKE_CODE "find_package(CppMicroServices NO_MODULE REQUIRED)") set(MODULE_INCLUDE_DIRS "\${CppMicroServices_INCLUDE_DIRS}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "\${US_RUNTIME_LIBRARY_DIRS}") set(CppMicroServices_CONFIG_FILE "${CMAKE_BINARY_DIR}/${MODULES_CONF_DIRNAME}/CppMicroServicesConfig.cmake" CACHE INTERNAL "Path to module config" FORCE) configure_file(${MITK_SOURCE_DIR}/CMake/moduleConf.cmake.in ${CppMicroServices_CONFIG_FILE} @ONLY) endfunction() _generate_cppmicroservices_conf() #----------------------------------------------------------------------------- # Add the MITK Core library #----------------------------------------------------------------------------- set(MITK_DEFAULT_SUBPROJECTS MITK-Core) add_subdirectory(Code) diff --git a/Core/Code/Common/mitkCoreServices.h b/Core/Code/Common/mitkCoreServices.h index 752f5b7119..d22ca60bd0 100644 --- a/Core/Code/Common/mitkCoreServices.h +++ b/Core/Code/Common/mitkCoreServices.h @@ -1,178 +1,178 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCORESERVICES_H #define MITKCORESERVICES_H #include "MitkCoreExports.h" #include #include #include -#include +#include #include #include namespace mitk { struct IMimeTypeProvider; struct IShaderRepository; class IPropertyAliases; class IPropertyDescriptions; class IPropertyExtensions; class IPropertyFilters; /** * @brief Access MITK core services. * * This class can be used to conveniently access common * MITK Core service objects. Some getter methods where implementations * exist in the core library are guaranteed to return a non-NULL service object. * * To ensure that CoreServices::Unget() is called after the caller * has finished using a service object, you should use the CoreServicePointer * helper class which calls Unget() when it goes out of scope: * * \code * CoreServicePointer shaderRepo(CoreServices::GetShaderRepository()); * // Do something with shaderRepo * \endcode * * @see CoreServicePointer */ class MITK_CORE_EXPORT CoreServices { public: /** * @brief Get an IShaderRepository instance. * @param context The module context of the module getting the service. * @return A IShaderRepository instance which can be NULL. */ static IShaderRepository* GetShaderRepository(); /** * @brief Get an IPropertyAliases instance. * @param context The module context of the module getting the service. * @return A non-NULL IPropertyAliases instance. */ static IPropertyAliases* GetPropertyAliases(us::ModuleContext* context = us::GetModuleContext()); /** * @brief Get an IPropertyDescriptions instance. * @param context The module context of the module getting the service. * @return A non-NULL IPropertyDescriptions instance. */ static IPropertyDescriptions* GetPropertyDescriptions(us::ModuleContext* context = us::GetModuleContext()); /** * @brief Get an IPropertyExtensions instance. * @param context The module context of the module getting the service. * @return A non-NULL IPropertyExtensions instance. */ static IPropertyExtensions* GetPropertyExtensions(us::ModuleContext* context = us::GetModuleContext()); /** * @brief Get an IPropertyFilters instance. * @param context The module context of the module getting the service. * @return A non-NULL IPropertyFilters instance. */ static IPropertyFilters* GetPropertyFilters(us::ModuleContext* context = us::GetModuleContext()); /** * @brief Get an IMimeTypeProvider instance. * @param context The module context of the module getting the service. * @return A non-NULL IMimeTypeProvider instance. */ static IMimeTypeProvider* GetMimeTypeProvider(us::ModuleContext* context = us::GetModuleContext()); /** * @brief Unget a previously acquired service instance. * @param service The service instance to be released. * @return \c true if ungetting the service was successful, \c false otherwise. */ template static bool Unget(S* service, us::ModuleContext* context = us::GetModuleContext()) { return Unget(context, us_service_interface_iid(), service); } private: static bool Unget(us::ModuleContext* context, const std::string& interfaceId, void* service); // purposely not implemented CoreServices(); CoreServices(const CoreServices&); CoreServices& operator=(const CoreServices&); }; /** * @brief A RAII helper class for core service objects. * * This is class is intended for usage in local scopes; it calls * CoreServices::Unget(S*) in its destructor. You should not construct * multiple CoreServicePointer instances using the same service pointer, * unless it is retrieved by a new call to a CoreServices getter method. * * @see CoreServices */ template class CoreServicePointer { public: explicit CoreServicePointer(S* service) : m_service(service) { assert(m_service); } ~CoreServicePointer() { try { CoreServices::Unget(m_service); } catch (const std::exception& e) { MITK_ERROR << e.what(); } catch (...) { MITK_ERROR << "Ungetting core service failed."; } } S* operator->() const { return m_service; } private: // purposely not implemented CoreServicePointer(const CoreServicePointer&); CoreServicePointer& operator=(const CoreServicePointer&); S* const m_service; }; } #endif // MITKCORESERVICES_H diff --git a/Modules/SceneSerialization/mitkSceneDataNodeReader.h b/Core/Code/Common/mitkServiceInterface.h similarity index 57% copy from Modules/SceneSerialization/mitkSceneDataNodeReader.h copy to Core/Code/Common/mitkServiceInterface.h index 58320d7e5e..c5ec79e154 100644 --- a/Modules/SceneSerialization/mitkSceneDataNodeReader.h +++ b/Core/Code/Common/mitkServiceInterface.h @@ -1,35 +1,34 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ +#ifndef mitkServiceInterface_h +#define mitkServiceInterface_h -#ifndef MITKSCENEDATANODEREADER_H -#define MITKSCENEDATANODEREADER_H +#include -#include +#ifdef Q_DECLARE_INTERFACE -namespace mitk { +#define MITK_DECLARE_SERVICE_INTERFACE(IFace, IId) \ + US_DECLARE_SERVICE_INTERFACE(IFace, IId) \ + Q_DECLARE_INTERFACE(IFace, IId) -class SceneDataNodeReader : public mitk::IDataNodeReader -{ +#else -public: +#define MITK_DECLARE_SERVICE_INTERFACE(IFace, IId) US_DECLARE_SERVICE_INTERFACE(IFace, IId) - int Read(const std::string& fileName, mitk::DataStorage& storage); -}; +#endif -} - -#endif // MITKSCENEDATANODEREADER_H +#endif diff --git a/Core/Code/Controllers/mitkCoreActivator.cpp b/Core/Code/Controllers/mitkCoreActivator.cpp deleted file mode 100644 index 86a0121d77..0000000000 --- a/Core/Code/Controllers/mitkCoreActivator.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "mitkRenderingManager.h" -#include "mitkPlanePositionManager.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void HandleMicroServicesMessages(us::MsgType type, const char* msg) -{ - switch (type) - { - case us::DebugMsg: - MITK_DEBUG << msg; - break; - case us::InfoMsg: - MITK_INFO << msg; - break; - case us::WarningMsg: - MITK_WARN << msg; - break; - case us::ErrorMsg: - MITK_ERROR << msg; - break; - } -} - -void AddMitkAutoLoadPaths(const std::string& programPath) -{ - us::ModuleSettings::AddAutoLoadPath(programPath); -#ifdef __APPLE__ - // Walk up three directories since that is where the .dylib files are located - // for build trees. - std::string additionalPath = programPath; - bool addPath = true; - for(int i = 0; i < 3; ++i) - { - std::size_t index = additionalPath.find_last_of('/'); - if (index != std::string::npos) - { - additionalPath = additionalPath.substr(0, index); - } - else - { - addPath = false; - break; - } - } - if (addPath) - { - us::ModuleSettings::AddAutoLoadPath(additionalPath); - } -#endif -} - -class ShaderRepositoryTracker : public us::ServiceTracker -{ - -public: - - ShaderRepositoryTracker() - : Superclass(us::GetModuleContext()) - { - } - - virtual void Close() - { - us::GetModuleContext()->RemoveModuleListener(this, &ShaderRepositoryTracker::HandleModuleEvent); - Superclass::Close(); - } - - virtual void Open() - { - us::GetModuleContext()->AddModuleListener(this, &ShaderRepositoryTracker::HandleModuleEvent); - Superclass::Open(); - } - -private: - - typedef us::ServiceTracker Superclass; - - TrackedType AddingService(const ServiceReferenceType &reference) - { - mitk::IShaderRepository* shaderRepo = Superclass::AddingService(reference); - if (shaderRepo) - { - // Add all existing shaders from modules to the new shader repository. - // If the shader repository is registered in a modules activator, the - // GetLoadedModules() function call below will also return the module - // which is currently registering the repository. The HandleModuleEvent - // method contains code to avoid double registrations due to a fired - // ModuleEvent::LOADED event after the activators Load() method finished. - std::vector modules = us::ModuleRegistry::GetLoadedModules(); - for (std::vector::const_iterator iter = modules.begin(), - endIter = modules.end(); iter != endIter; ++iter) - { - this->AddModuleShaderToRepository(*iter, shaderRepo); - } - - m_ShaderRepositories.push_back(shaderRepo); - } - return shaderRepo; - } - - void RemovedService(const ServiceReferenceType& /*reference*/, TrackedType tracked) - { - m_ShaderRepositories.erase(std::remove(m_ShaderRepositories.begin(), m_ShaderRepositories.end(), tracked), - m_ShaderRepositories.end()); - } - - void HandleModuleEvent(const us::ModuleEvent moduleEvent) - { - if (moduleEvent.GetType() == us::ModuleEvent::LOADED) - { - std::vector shaderRepos; - for (std::map > >::const_iterator shaderMapIter = m_ModuleIdToShaderIds.begin(), - shaderMapEndIter = m_ModuleIdToShaderIds.end(); shaderMapIter != shaderMapEndIter; ++shaderMapIter) - { - if (shaderMapIter->second.find(moduleEvent.GetModule()->GetModuleId()) == shaderMapIter->second.end()) - { - shaderRepos.push_back(shaderMapIter->first); - } - } - AddModuleShadersToRepositories(moduleEvent.GetModule(), shaderRepos); - } - else if (moduleEvent.GetType() == us::ModuleEvent::UNLOADED) - { - RemoveModuleShadersFromRepositories(moduleEvent.GetModule(), m_ShaderRepositories); - } - } - - void AddModuleShadersToRepositories(us::Module* module, const std::vector& shaderRepos) - { - // search and load shader files - std::vector shaderResources = module->FindResources("Shaders", "*.xml", true); - for (std::vector::iterator i = shaderResources.begin(); - i != shaderResources.end(); ++i) - { - if (*i) - { - us::ModuleResourceStream rs(*i); - for (std::vector::const_iterator shaderRepoIter = shaderRepos.begin(), - shaderRepoEndIter = shaderRepos.end(); shaderRepoIter != shaderRepoEndIter; ++shaderRepoIter) - { - int id = (*shaderRepoIter)->LoadShader(rs, i->GetBaseName()); - if (id >= 0) - { - m_ModuleIdToShaderIds[*shaderRepoIter][module->GetModuleId()].push_back(id); - } - } - rs.seekg(0, std::ios_base::beg); - } - } - } - - void AddModuleShaderToRepository(us::Module* module, mitk::IShaderRepository* shaderRepo) - { - std::vector shaderRepos; - shaderRepos.push_back(shaderRepo); - this->AddModuleShadersToRepositories(module, shaderRepos); - } - - void RemoveModuleShadersFromRepositories(us::Module* module, - const std::vector& shaderRepos) - { - for (std::vector::const_iterator shaderRepoIter = shaderRepos.begin(), - shaderRepoEndIter = shaderRepos.end(); shaderRepoIter != shaderRepoEndIter; ++shaderRepoIter) - { - std::map >& moduleIdToShaderIds = m_ModuleIdToShaderIds[*shaderRepoIter]; - std::map >::iterator shaderIdsIter = - moduleIdToShaderIds.find(module->GetModuleId()); - if (shaderIdsIter != moduleIdToShaderIds.end()) - { - for (std::vector::iterator idIter = shaderIdsIter->second.begin(); - idIter != shaderIdsIter->second.end(); ++idIter) - { - (*shaderRepoIter)->UnloadShader(*idIter); - } - moduleIdToShaderIds.erase(shaderIdsIter); - } - } - } - -private: - - // Maps to each shader repository a map containing module ids and related - // shader registration ids - std::map > > m_ModuleIdToShaderIds; - std::vector m_ShaderRepositories; -}; - -/* - * This is the module activator for the "Mitk" module. It registers core services - * like ... - */ -class MitkCoreActivator : public us::ModuleActivator -{ -public: - - void Load(us::ModuleContext* context) - { - // Handle messages from CppMicroServices - us::installMsgHandler(HandleMicroServicesMessages); - - // Add the current application directory to the auto-load paths. - // This is useful for third-party executables. - std::string programPath = mitk::IOUtil::GetProgramPath(); - if (programPath.empty()) - { - MITK_WARN << "Could not get the program path."; - } - else - { - AddMitkAutoLoadPaths(programPath); - } - - //m_RenderingManager = mitk::RenderingManager::New(); - //context->RegisterService(renderingManager.GetPointer()); - m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); - context->RegisterService(m_PlanePositionManager.get()); - - m_CoreDataNodeReader.reset(new mitk::CoreDataNodeReader); - context->RegisterService(m_CoreDataNodeReader.get()); - - m_PropertyAliases.reset(new mitk::PropertyAliases); - context->RegisterService(m_PropertyAliases.get()); - - m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); - context->RegisterService(m_PropertyDescriptions.get()); - - m_PropertyExtensions.reset(new mitk::PropertyExtensions); - context->RegisterService(m_PropertyExtensions.get()); - - m_PropertyFilters.reset(new mitk::PropertyFilters); - context->RegisterService(m_PropertyFilters.get()); - - m_ShaderRepositoryTracker.Open(); - - /* - There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. - This code is left here as a reminder, just in case we might need to do that some time. - - vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); - vtkObjectFactory::RegisterFactory( textureFactory ); - textureFactory->Delete(); - */ - } - - void Unload(us::ModuleContext* ) - { - // The mitk::ModuleContext* argument of the Unload() method - // will always be 0 for the Mitk library. It makes no sense - // to use it at this stage anyway, since all libraries which - // know about the module system have already been unloaded. - - m_ShaderRepositoryTracker.Close(); - } - -private: - - ShaderRepositoryTracker m_ShaderRepositoryTracker; - - //mitk::RenderingManager::Pointer m_RenderingManager; - std::auto_ptr m_PlanePositionManager; - std::auto_ptr m_CoreDataNodeReader; - std::auto_ptr m_PropertyAliases; - std::auto_ptr m_PropertyDescriptions; - std::auto_ptr m_PropertyExtensions; - std::auto_ptr m_PropertyFilters; -}; - -US_EXPORT_MODULE_ACTIVATOR(MitkCore, MitkCoreActivator) - -// Call CppMicroservices initialization code at the end of the file. -// This especially ensures that VTK object factories have already -// been registered (VTK initialization code is injected by implicitly -// include VTK header files at the top of this file). -US_INITIALIZE_MODULE("MitkCore", "MitkCore") diff --git a/Core/Code/Controllers/mitkPlanePositionManager.h b/Core/Code/Controllers/mitkPlanePositionManager.h index cddb1e9f95..9c71be65fb 100644 --- a/Core/Code/Controllers/mitkPlanePositionManager.h +++ b/Core/Code/Controllers/mitkPlanePositionManager.h @@ -1,94 +1,94 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPlanePositionManager_h_Included #define mitkPlanePositionManager_h_Included #include "mitkCommon.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkDataStorage.h" -#include +#include #include class MitkCoreActivator; namespace mitk { /** The mitk::PlanePositionManagerService holds and manages a list of certain planepositions. To store a new position you need to specify the first slice of your slicestack and the slicenumber you want to restore in the mitk::PlanePositionManager::AddNewPlanePosition() function. To restore a position call mitk::PlanePositionManagerService::GetPlanePosition(ID) where ID is the position in the plane positionlist (returned by AddNewPlanePostion). This will give a mitk::RestorePlanePositionOperation which can be executed by the SliceNavigationController of the slicestack. \sa QmitkSegmentationView.cpp */ class MITK_CORE_EXPORT PlanePositionManagerService { public: PlanePositionManagerService(); ~PlanePositionManagerService(); /** \brief Adds a new plane position to the list. If this geometry is identical to one of the list nothing will be added \a plane THE FIRST! slice of the slice stack \a sliceIndex the slice number of the selected slice \return returns the ID i.e. the position in the positionlist. If the PlaneGeometry which is to be added already exists the existing ID will be returned. */ unsigned int AddNewPlanePosition(const mitk::PlaneGeometry* plane, unsigned int sliceIndex = 0); /** \brief Removes the plane at the position \a ID from the list. \a ID the plane ID which should be removed, i.e. its position in the list \return true if the plane was removed successfully and false if it is an invalid ID */ bool RemovePlanePosition(unsigned int ID); /// \brief Clears the complete positionlist void RemoveAllPlanePositions(); /** \brief Getter for a specific plane position with a given ID \a ID the ID of the plane position \return Returns a RestorePlanePositionOperation which can be executed by th SliceNavigationController or NULL for an invalid ID */ mitk::RestorePlanePositionOperation* GetPlanePosition( unsigned int ID); /// \brief Getting the number of all stored planes unsigned int GetNumberOfPlanePositions(); private: // Disable copy constructor and assignment operator. PlanePositionManagerService(const PlanePositionManagerService&); PlanePositionManagerService& operator=(const PlanePositionManagerService&); std::vector m_PositionList; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::PlanePositionManagerService, "org.mitk.PlanePositionManagerService") +MITK_DECLARE_SERVICE_INTERFACE(mitk::PlanePositionManagerService, "org.mitk.PlanePositionManagerService") #endif diff --git a/Core/Code/IO/mitkAbstractFileIO.cpp b/Core/Code/IO/mitkAbstractFileIO.cpp index 0789b88c60..4fd52a6613 100644 --- a/Core/Code/IO/mitkAbstractFileIO.cpp +++ b/Core/Code/IO/mitkAbstractFileIO.cpp @@ -1,193 +1,194 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkAbstractFileIO.h" #include "mitkCustomMimeType.h" namespace mitk { AbstractFileIO::Options AbstractFileIO::GetReaderOptions() const { return AbstractFileReader::GetOptions(); } us::Any AbstractFileIO::GetReaderOption(const std::string& name) const { return AbstractFileReader::GetOption(name); } void AbstractFileIO::SetReaderOptions(const AbstractFileIO::Options& options) { AbstractFileReader::SetOptions(options); } void AbstractFileIO::SetReaderOption(const std::string& name, const us::Any& value) { AbstractFileReader::SetOption(name, value); } AbstractFileIO::Options AbstractFileIO::GetWriterOptions() const { return AbstractFileWriter::GetOptions(); } us::Any AbstractFileIO::GetWriterOption(const std::string& name) const { return AbstractFileWriter::GetOption(name); } void AbstractFileIO::SetWriterOptions(const AbstractFileIO::Options& options) { AbstractFileWriter::SetOptions(options); } void AbstractFileIO::SetWriterOption(const std::string& name, const us::Any& value) { AbstractFileWriter::SetOption(name, value); } IFileIO::ConfidenceLevel AbstractFileIO::GetReaderConfidenceLevel() const { return AbstractFileIOReader::GetReaderConfidenceLevel(); } IFileIO::ConfidenceLevel AbstractFileIO::GetWriterConfidenceLevel() const { return AbstractFileIOWriter::GetWriterConfidenceLevel(); } std::pair, us::ServiceRegistration > AbstractFileIO::RegisterService(us::ModuleContext* context) { std::pair, us::ServiceRegistration > result; result.first = this->AbstractFileReader::RegisterService(context); - CustomMimeType writerMimeType = this->AbstractFileWriter::GetMimeType(); - if (writerMimeType.GetName().empty() && writerMimeType.GetExtensions().empty()) + const CustomMimeType* writerMimeType = this->AbstractFileWriter::GetMimeType(); + if (writerMimeType == NULL || + (writerMimeType->GetName().empty() && writerMimeType->GetExtensions().empty())) { this->AbstractFileWriter::SetMimeType(CustomMimeType(this->AbstractFileReader::GetRegisteredMimeType().GetName())); } result.second = this->AbstractFileWriter::RegisterService(context); return result; } AbstractFileIO::AbstractFileIO(const AbstractFileIO& other) : AbstractFileIOReader(other) , AbstractFileIOWriter(other) { } AbstractFileIO::AbstractFileIO(const std::string& baseDataType) : AbstractFileIOReader() , AbstractFileIOWriter(baseDataType) { } AbstractFileIO::AbstractFileIO(const std::string& baseDataType, const CustomMimeType& mimeType, const std::string& description) : AbstractFileIOReader(mimeType, description) , AbstractFileIOWriter(baseDataType, CustomMimeType(mimeType.GetName()), description) { } void AbstractFileIO::SetMimeType(const CustomMimeType& mimeType) { this->AbstractFileReader::SetMimeType(mimeType); this->AbstractFileWriter::SetMimeType(CustomMimeType(mimeType.GetName())); } -CustomMimeType AbstractFileIO::GetMimeType() const +const CustomMimeType* AbstractFileIO::GetMimeType() const { - CustomMimeType mimeType = this->AbstractFileReader::GetMimeType(); - if (mimeType.GetName() != this->AbstractFileWriter::GetMimeType().GetName()) + const CustomMimeType* mimeType = this->AbstractFileReader::GetMimeType(); + if (mimeType->GetName() != this->AbstractFileWriter::GetMimeType()->GetName()) { MITK_WARN << "Reader and writer mime-tpyes are different, using the mime-type from IFileReader"; } return mimeType; } void AbstractFileIO::SetReaderDescription(const std::string& description) { this->AbstractFileReader::SetDescription(description); } std::string AbstractFileIO::GetReaderDescription() const { return this->AbstractFileReader::GetDescription(); } void AbstractFileIO::SetWriterDescription(const std::string& description) { this->AbstractFileWriter::SetDescription(description); } std::string AbstractFileIO::GetWriterDescription() const { return this->AbstractFileWriter::GetDescription(); } void AbstractFileIO::SetDefaultReaderOptions(const AbstractFileIO::Options& defaultOptions) { this->AbstractFileReader::SetDefaultOptions(defaultOptions); } AbstractFileIO::Options AbstractFileIO::GetDefaultReaderOptions() const { return this->AbstractFileReader::GetDefaultOptions(); } void AbstractFileIO::SetDefaultWriterOptions(const AbstractFileIO::Options& defaultOptions) { this->AbstractFileWriter::SetDefaultOptions(defaultOptions); } AbstractFileIO::Options AbstractFileIO::GetDefaultWriterOptions() const { return this->AbstractFileWriter::GetDefaultOptions(); } void AbstractFileIO::SetReaderRanking(int ranking) { this->AbstractFileReader::SetRanking(ranking); } int AbstractFileIO::GetReaderRanking() const { return this->AbstractFileReader::GetRanking(); } void AbstractFileIO::SetWriterRanking(int ranking) { this->AbstractFileWriter::SetRanking(ranking); } int AbstractFileIO::GetWriterRanking() const { return this->AbstractFileWriter::GetRanking(); } IFileReader* AbstractFileIO::ReaderClone() const { return this->IOClone(); } IFileWriter* AbstractFileIO::WriterClone() const { return this->IOClone(); } } diff --git a/Core/Code/IO/mitkAbstractFileIO.h b/Core/Code/IO/mitkAbstractFileIO.h index b66a73dce3..3791432ac0 100644 --- a/Core/Code/IO/mitkAbstractFileIO.h +++ b/Core/Code/IO/mitkAbstractFileIO.h @@ -1,192 +1,192 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKABSTRACTFILEIO_H #define MITKABSTRACTFILEIO_H #include "mitkAbstractFileReader.h" #include "mitkAbstractFileWriter.h" namespace mitk { #ifndef DOXYGEN_SKIP // Skip this code during Doxygen processing, because it only // exists to resolve name clashes when inheriting from both // AbstractFileReader and AbstractFileWriter. class AbstractFileIOReader : public AbstractFileReader { public: virtual ConfidenceLevel GetReaderConfidenceLevel() const { return AbstractFileReader::GetConfidenceLevel(); } virtual ConfidenceLevel GetConfidenceLevel() const { return this->GetReaderConfidenceLevel(); } protected: AbstractFileIOReader() {} AbstractFileIOReader(const CustomMimeType& mimeType, const std::string& description) : AbstractFileReader(mimeType, description) {} private: virtual IFileReader* ReaderClone() const = 0; IFileReader* Clone() const { return ReaderClone(); } }; struct AbstractFileIOWriter : public AbstractFileWriter { virtual ConfidenceLevel GetWriterConfidenceLevel() const { return AbstractFileWriter::GetConfidenceLevel(); } virtual ConfidenceLevel GetConfidenceLevel() const { return this->GetWriterConfidenceLevel(); } protected: AbstractFileIOWriter(const std::string& baseDataType) : AbstractFileWriter(baseDataType) {} AbstractFileIOWriter(const std::string& baseDataType, const CustomMimeType& mimeType, const std::string& description) : AbstractFileWriter(baseDataType, mimeType, description) {} private: virtual IFileWriter* WriterClone() const = 0; IFileWriter* Clone() const { return WriterClone(); } }; #endif // DOXYGEN_SKIP class MITK_CORE_EXPORT AbstractFileIO : public AbstractFileIOReader, public AbstractFileIOWriter { public: Options GetReaderOptions() const; us::Any GetReaderOption(const std::string &name) const; void SetReaderOptions(const Options& options); void SetReaderOption(const std::string& name, const us::Any& value); Options GetWriterOptions() const; us::Any GetWriterOption(const std::string &name) const; void SetWriterOptions(const Options& options); void SetWriterOption(const std::string& name, const us::Any& value); virtual ConfidenceLevel GetReaderConfidenceLevel() const; virtual ConfidenceLevel GetWriterConfidenceLevel() const; std::pair, us::ServiceRegistration > RegisterService(us::ModuleContext* context = us::GetModuleContext()); protected: AbstractFileIO(const AbstractFileIO& other); AbstractFileIO(const std::string& baseDataType); /** * Associate this reader instance with the given MIME type. * * @param mimeType The mime type this reader can read. * @param description A human readable description of this reader. * * @throws std::invalid_argument if \c mimeType is empty. * * @see RegisterService */ explicit AbstractFileIO(const std::string& baseDataType, const CustomMimeType& mimeType, const std::string& description); /** * Associate this reader with the given file extension. * * Additonal file extensions can be added by sub-classes by calling AddExtension * or SetExtensions on the CustomMimeType object returned by GetMimeType() and * setting the modified object again via SetMimeType(). * * @param extension The file extension (without a leading period) for which a registered * mime-type object is looked up and associated with this instance. * @param description A human readable description of this reader. * * @see RegisterService */ explicit AbstractFileIO(const std::string& baseDataType, const std::string& extension, const std::string& description); void SetMimeType(const CustomMimeType& mimeType); /** * @return The mime-type this reader can handle. */ - CustomMimeType GetMimeType() const; + const CustomMimeType* GetMimeType() const; void SetReaderDescription(const std::string& description); std::string GetReaderDescription() const; void SetWriterDescription(const std::string& description); std::string GetWriterDescription() const; void SetDefaultReaderOptions(const Options& defaultOptions); Options GetDefaultReaderOptions() const; void SetDefaultWriterOptions(const Options& defaultOptions); Options GetDefaultWriterOptions() const; /** * \brief Set the service ranking for this file reader. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which reader to use if several * equivalent readers have been found. * It may be used to replace a default reader from MITK in your own project. * E.g. if you want to use your own reader for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetReaderRanking(int ranking); int GetReaderRanking() const; void SetWriterRanking(int ranking); int GetWriterRanking() const; private: AbstractFileIO& operator=(const AbstractFileIO& other); virtual AbstractFileIO* IOClone() const = 0; virtual IFileReader* ReaderClone() const; virtual IFileWriter* WriterClone() const; }; } #endif // MITKABSTRACTFILEIO_H diff --git a/Core/Code/IO/mitkAbstractFileReader.cpp b/Core/Code/IO/mitkAbstractFileReader.cpp index c03e03d3fd..411e83bd99 100644 --- a/Core/Code/IO/mitkAbstractFileReader.cpp +++ b/Core/Code/IO/mitkAbstractFileReader.cpp @@ -1,399 +1,414 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include +#include #include #include #include #include #include #include namespace mitk { AbstractFileReader::InputStream::InputStream(IFileReader* reader, std::ios_base::openmode mode) : std::istream(NULL) , m_Stream(NULL) { std::istream* stream = reader->GetInputStream(); if (stream) { this->init(stream->rdbuf()); } else { m_Stream = new std::ifstream(reader->GetInputLocation().c_str(), mode); this->init(m_Stream->rdbuf()); } } AbstractFileReader::InputStream::~InputStream() { delete m_Stream; } class AbstractFileReader::Impl : public FileReaderWriterBase { public: Impl() : FileReaderWriterBase() , m_Stream(NULL) , m_PrototypeFactory(NULL) {} Impl(const Impl& other) : FileReaderWriterBase(other) , m_Stream(NULL) , m_PrototypeFactory(NULL) {} std::string m_Location; std::string m_TmpFile; std::istream* m_Stream; us::PrototypeServiceFactory* m_PrototypeFactory; us::ServiceRegistration m_Reg; }; AbstractFileReader::AbstractFileReader() : d(new Impl) { } AbstractFileReader::~AbstractFileReader() { UnregisterService(); delete d->m_PrototypeFactory; if (!d->m_TmpFile.empty()) { std::remove(d->m_TmpFile.c_str()); } } AbstractFileReader::AbstractFileReader(const AbstractFileReader& other) : IFileReader(), d(new Impl(*other.d.get())) - { } AbstractFileReader::AbstractFileReader(const CustomMimeType& mimeType, const std::string& description) : d(new Impl) { d->SetMimeType(mimeType); d->SetDescription(description); } ////////////////////// Reading ///////////////////////// +std::vector AbstractFileReader::Read() +{ + std::vector result; + + DataStorage::Pointer ds = StandaloneDataStorage::New().GetPointer(); + this->Read(*ds); + DataStorage::SetOfObjects::ConstPointer dataNodes = ds->GetAll(); + for (DataStorage::SetOfObjects::ConstIterator iter = dataNodes->Begin(), + iterEnd = dataNodes->End(); iter != iterEnd; ++iter) + { + result.push_back(iter.Value()->GetData()); + } + return result; +} + DataStorage::SetOfObjects::Pointer AbstractFileReader::Read(DataStorage& ds) { DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); std::vector data = this->Read(); for (std::vector::iterator iter = data.begin(); iter != data.end(); ++iter) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); this->SetDefaultDataNodeProperties(node, this->GetInputLocation()); ds.Add(node); result->InsertElement(result->Size(), node); } return result; } IFileReader::ConfidenceLevel AbstractFileReader::GetConfidenceLevel() const { if (d->m_Stream) { if (*d->m_Stream) return Supported; } else { if (itksys::SystemTools::FileExists(this->GetInputLocation().c_str(), true)) { return Supported; } } return Unsupported; } //////////// µS Registration & Properties ////////////// us::ServiceRegistration AbstractFileReader::RegisterService(us::ModuleContext* context) { if (d->m_PrototypeFactory) return us::ServiceRegistration(); if(context == NULL) { context = us::GetModuleContext(); } d->RegisterMimeType(context); - if (this->GetMimeType().GetName().empty()) + if (this->GetMimeType()->GetName().empty()) { MITK_WARN << "Not registering reader due to empty MIME type."; return us::ServiceRegistration(); } struct PrototypeFactory : public us::PrototypeServiceFactory { AbstractFileReader* const m_Prototype; PrototypeFactory(AbstractFileReader* prototype) : m_Prototype(prototype) {} us::InterfaceMap GetService(us::Module* /*module*/, const us::ServiceRegistrationBase& /*registration*/) { return us::MakeInterfaceMap(m_Prototype->Clone()); } void UngetService(us::Module* /*module*/, const us::ServiceRegistrationBase& /*registration*/, const us::InterfaceMap& service) { delete us::ExtractInterface(service); } }; d->m_PrototypeFactory = new PrototypeFactory(this); us::ServiceProperties props = this->GetServiceProperties(); d->m_Reg = context->RegisterService(d->m_PrototypeFactory, props); return d->m_Reg; } void AbstractFileReader::UnregisterService() { try { d->m_Reg.Unregister(); } catch (const std::exception&) {} } us::ServiceProperties AbstractFileReader::GetServiceProperties() const { us::ServiceProperties result; result[IFileReader::PROP_DESCRIPTION()] = this->GetDescription(); - result[IFileReader::PROP_MIMETYPE()] = this->GetMimeType().GetName(); + result[IFileReader::PROP_MIMETYPE()] = this->GetMimeType()->GetName(); result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking(); return result; } us::ServiceRegistration AbstractFileReader::RegisterMimeType(us::ModuleContext* context) { return d->RegisterMimeType(context); } void AbstractFileReader::SetMimeType(const CustomMimeType& mimeType) { d->SetMimeType(mimeType); } void AbstractFileReader::SetDescription(const std::string& description) { d->SetDescription(description); } void AbstractFileReader::SetRanking(int ranking) { d->SetRanking(ranking); } int AbstractFileReader::GetRanking() const { return d->GetRanking(); } std::string AbstractFileReader::GetLocalFileName() const { std::string localFileName; if (d->m_Stream) { if (d->m_TmpFile.empty()) { // write the stream contents to temporary file std::string ext = itksys::SystemTools::GetFilenameExtension(this->GetInputLocation()); std::ofstream tmpStream; localFileName = mitk::IOUtil::CreateTemporaryFile(tmpStream, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary, "XXXXXX" + ext); tmpStream << d->m_Stream->rdbuf(); d->m_TmpFile = localFileName; } else { localFileName = d->m_TmpFile; } } else { localFileName = d->m_Location; } return localFileName; } //////////////////////// Options /////////////////////// void AbstractFileReader::SetDefaultOptions(const IFileReader::Options& defaultOptions) { d->SetDefaultOptions(defaultOptions); } IFileReader::Options AbstractFileReader::GetDefaultOptions() const { return d->GetDefaultOptions(); } void AbstractFileReader::SetInput(const std::string& location) { d->m_Location = location; d->m_Stream = NULL; } void AbstractFileReader::SetInput(const std::string& location, std::istream* is) { if (d->m_Stream != is && !d->m_TmpFile.empty()) { std::remove(d->m_TmpFile.c_str()); d->m_TmpFile.clear(); } d->m_Location = location; d->m_Stream = is; } std::string AbstractFileReader::GetInputLocation() const { return d->m_Location; } std::istream*AbstractFileReader::GetInputStream() const { return d->m_Stream; } MimeType AbstractFileReader::GetRegisteredMimeType() const { return d->GetRegisteredMimeType(); } IFileReader::Options AbstractFileReader::GetOptions() const { return d->GetOptions(); } us::Any AbstractFileReader::GetOption(const std::string& name) const { return d->GetOption(name); } void AbstractFileReader::SetOptions(const Options& options) { d->SetOptions(options); } void AbstractFileReader::SetOption(const std::string& name, const us::Any& value) { d->SetOption(name, value); } ////////////////// MISC ////////////////// void AbstractFileReader::AddProgressCallback(const ProgressCallback& callback) { d->AddProgressCallback(callback); } void AbstractFileReader::RemoveProgressCallback(const ProgressCallback& callback) { d->RemoveProgressCallback(callback); } ////////////////// µS related Getters ////////////////// -CustomMimeType AbstractFileReader::GetMimeType() const +const CustomMimeType* AbstractFileReader::GetMimeType() const { return d->GetMimeType(); } void AbstractFileReader::SetMimeTypePrefix(const std::string& prefix) { d->SetMimeTypePrefix(prefix); } std::string AbstractFileReader::GetMimeTypePrefix() const { return d->GetMimeTypePrefix(); } std::string AbstractFileReader::GetDescription() const { return d->GetDescription(); } void AbstractFileReader::SetDefaultDataNodeProperties(DataNode* node, const std::string& filePath) { // path if (!filePath.empty()) { mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenamePath(filePath) ); node->SetProperty(StringProperty::PATH, pathProp); } // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if(nameProp.IsNull() || (strcmp(nameProp->GetValue(),"No Name!")==0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer() ); if(baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(),"No Name!")==0)) { // name neither defined in node, nor in BaseData -> name = filebasename; nameProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenameWithoutExtension(itksys::SystemTools::GetFilenameName(filePath))); node->SetProperty("name", nameProp); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue()); node->SetProperty("name", nameProp); } } // visibility if(!node->GetProperty("visible")) { node->SetVisibility(true); } } } diff --git a/Core/Code/IO/mitkAbstractFileReader.h b/Core/Code/IO/mitkAbstractFileReader.h index de905aa836..dbcccfa3d3 100644 --- a/Core/Code/IO/mitkAbstractFileReader.h +++ b/Core/Code/IO/mitkAbstractFileReader.h @@ -1,231 +1,231 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 #define AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 // Macro #include // MITK #include #include #include // Microservices #include #include #include namespace us { struct PrototypeServiceFactory; } namespace mitk { class CustomMimeType; /** * @brief Base class for creating mitk::BaseData objects from files or streams. * @ingroup IO */ class MITK_CORE_EXPORT AbstractFileReader : public mitk::IFileReader { public: virtual void SetInput(const std::string& location); virtual void SetInput(const std::string &location, std::istream* is); virtual std::string GetInputLocation() const; virtual std::istream* GetInputStream() const; MimeType GetRegisteredMimeType() const; /** * @brief Reads a path or stream and creates a list of BaseData objects. * * This method must be implemented for each specific reader. Call * GetInputStream() first and check for a non-null stream to read from. * If the input stream is \c NULL, use GetInputLocation() to read from a local * file-system path. * * If the reader cannot use streams directly, use GetLocalFileName() instead. * * @return The created BaseData objects. * @throws mitk::Exception * * @see GetLocalFileName() * @see IFileReader::Read() */ virtual std::vector > Read() = 0; virtual DataStorage::SetOfObjects::Pointer Read(mitk::DataStorage& ds); virtual ConfidenceLevel GetConfidenceLevel() const; virtual Options GetOptions() const; virtual us::Any GetOption(const std::string &name) const; virtual void SetOptions(const Options& options); virtual void SetOption(const std::string& name, const us::Any& value); virtual void AddProgressCallback(const ProgressCallback& callback); virtual void RemoveProgressCallback(const ProgressCallback& callback); /** * Associate this reader with the MIME type returned by the current IMimeTypeProvider * service for the provided extension if the MIME type exists, otherwise registers * a new MIME type when RegisterService() is called. * * If no MIME type for \c extension is already registered, a call to RegisterService() * will register a new MIME type and associate this reader instance with it. The MIME * type id can be set via SetMimeType() or it will be auto-generated using \c extension, * having the form "application/vnd.mitk.". * * @param extension The file extension (without a leading period) for which a registered * mime-type object is looked up and associated with this reader instance. * @param description A human readable description of this reader. */ us::ServiceRegistration RegisterService(us::ModuleContext* context = us::GetModuleContext()); void UnregisterService(); protected: /** * @brief An input stream wrapper. * * If a reader can only work with input streams, use an instance * of this class to either wrap the specified input stream or * create a new input stream based on the input location in the * file system. */ class InputStream : public std::istream { public: InputStream(IFileReader* writer, std::ios_base::openmode mode = std::ios_base::in); ~InputStream(); private: std::istream* m_Stream; }; AbstractFileReader(); ~AbstractFileReader(); AbstractFileReader(const AbstractFileReader& other); /** * Associate this reader instance with the given MIME type. * * If \c mimeType does not provide an extension list, an already * registered mime-type object is used. Otherwise, the first entry in * the extensions list is used to construct a mime-type name and * register it as a new CustomMimeType service object in the default * implementation of RegisterMimeType(). * * @param mimeType The mime type this reader can read. * @param description A human readable description of this reader. * * @throws std::invalid_argument if \c mimeType is empty. * * @see RegisterService */ explicit AbstractFileReader(const CustomMimeType& mimeType, const std::string& description); virtual us::ServiceProperties GetServiceProperties() const; /** * Registers a new CustomMimeType service object. * * This method is called from RegisterService and the default implementation * registers a new mime-type service object if all of the following conditions * are true: * * - TODO * * @param context * @return * @throws std::invalid_argument if \c context is NULL. */ virtual us::ServiceRegistration RegisterMimeType(us::ModuleContext* context); void SetMimeType(const CustomMimeType& mimeType); /** * @return The mime-type this reader can handle. */ - CustomMimeType GetMimeType() const; + const CustomMimeType* GetMimeType() const; void SetMimeTypePrefix(const std::string& prefix); std::string GetMimeTypePrefix() const; void SetDescription(const std::string& description); std::string GetDescription() const; void SetDefaultOptions(const Options& defaultOptions); Options GetDefaultOptions() const; /** * \brief Set the service ranking for this file reader. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which reader to use if several * equivalent readers have been found. * It may be used to replace a default reader from MITK in your own project. * E.g. if you want to use your own reader for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetRanking(int ranking); int GetRanking() const; /** * @brief Get a local file name for reading. * * This is a convenience method for readers which cannot work natively * with input streams. If no input stream has been been set, * this method just returns the result of GetLocation(). However, if * SetLocation(std::string, std::istream*) has been called with a non-null * input stream, this method writes the contents of the stream to a temporary * file and returns the name of the temporary file. * * The temporary file is deleted when either SetLocation(std::string, std::istream*) * is called again with a different input stream or the destructor of this * class is called. * * This method does not validate file names set via SetInput(std::string). * * @return A file path in the local file-system for reading. */ std::string GetLocalFileName() const; virtual void SetDefaultDataNodeProperties(DataNode* node, const std::string& filePath); private: AbstractFileReader& operator=(const AbstractFileReader& other); virtual mitk::IFileReader* Clone() const = 0; class Impl; std::auto_ptr d; }; } // namespace mitk #endif /* AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Core/Code/IO/mitkAbstractFileWriter.cpp b/Core/Code/IO/mitkAbstractFileWriter.cpp index 0fa5c0a066..41b7c5e4df 100644 --- a/Core/Code/IO/mitkAbstractFileWriter.cpp +++ b/Core/Code/IO/mitkAbstractFileWriter.cpp @@ -1,395 +1,395 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include namespace mitk { struct AbstractFileWriter::LocalFile::Impl { Impl(const std::string& location, std::ostream* os) : m_Location(location) , m_Stream(os) {} std::string m_Location; std::string m_TmpFileName; std::ostream* m_Stream; }; AbstractFileWriter::LocalFile::LocalFile(IFileWriter *writer) : d(new Impl(writer->GetOutputLocation(), writer->GetOutputStream())) { } AbstractFileWriter::LocalFile::~LocalFile() { if (d->m_Stream && !d->m_TmpFileName.empty()) { std::ifstream ifs(d->m_TmpFileName.c_str(), std::ios_base::binary); *d->m_Stream << ifs.rdbuf(); d->m_Stream->flush(); ifs.close(); std::remove(d->m_TmpFileName.c_str()); } } std::string AbstractFileWriter::LocalFile::GetFileName() { if (d->m_Stream == NULL) { return d->m_Location; } else if (d->m_TmpFileName.empty()) { std::string ext = itksys::SystemTools::GetFilenameExtension(d->m_Location); d->m_TmpFileName = IOUtil::CreateTemporaryFile("XXXXXX" + ext); } return d->m_TmpFileName; } AbstractFileWriter::OutputStream::OutputStream(IFileWriter* writer, std::ios_base::openmode mode) : std::ostream(NULL) , m_Stream(NULL) { std::ostream* stream = writer->GetOutputStream(); if (stream) { this->init(stream->rdbuf()); } else { m_Stream = new std::ofstream(writer->GetOutputLocation().c_str(), mode); this->init(m_Stream->rdbuf()); } } AbstractFileWriter::OutputStream::~OutputStream() { delete m_Stream; } class AbstractFileWriter::Impl : public FileReaderWriterBase { public: Impl() : FileReaderWriterBase() , m_BaseData(NULL) , m_Stream(NULL) , m_PrototypeFactory(NULL) {} Impl(const Impl& other) : FileReaderWriterBase(other) , m_BaseDataType(other.m_BaseDataType) , m_BaseData(NULL) , m_Stream(NULL) , m_PrototypeFactory(NULL) {} std::string m_BaseDataType; const BaseData* m_BaseData; std::string m_Location; std::ostream* m_Stream; us::PrototypeServiceFactory* m_PrototypeFactory; us::ServiceRegistration m_Reg; }; void AbstractFileWriter::SetInput(const BaseData* data) { d->m_BaseData = data; } const BaseData* AbstractFileWriter::GetInput() const { return d->m_BaseData; } void AbstractFileWriter::SetOutputLocation(const std::string& location) { d->m_Location = location; d->m_Stream = NULL; } std::string AbstractFileWriter::GetOutputLocation() const { return d->m_Location; } void AbstractFileWriter::SetOutputStream(const std::string& location, std::ostream* os) { d->m_Location = location; d->m_Stream = os; } std::ostream* AbstractFileWriter::GetOutputStream() const { return d->m_Stream; } AbstractFileWriter::~AbstractFileWriter() { UnregisterService(); delete d->m_PrototypeFactory; } AbstractFileWriter::AbstractFileWriter(const AbstractFileWriter& other) : IFileWriter(), d(new Impl(*other.d.get())) { } AbstractFileWriter::AbstractFileWriter(const std::string& baseDataType) : d(new Impl) { d->m_BaseDataType = baseDataType; } AbstractFileWriter::AbstractFileWriter(const std::string& baseDataType, const CustomMimeType& mimeType, const std::string& description) : d(new Impl) { d->m_BaseDataType = baseDataType; d->SetMimeType(mimeType); d->SetDescription(description); } ////////////////////// Writing ///////////////////////// IFileWriter::ConfidenceLevel AbstractFileWriter::GetConfidenceLevel() const { if (d->m_BaseData == NULL) return Unsupported; std::vector classHierarchy = d->m_BaseData->GetClassHierarchy(); if (std::find(classHierarchy.begin(), classHierarchy.end(), d->m_BaseDataType) == classHierarchy.end()) { return Unsupported; } return Supported; } MimeType AbstractFileWriter::GetRegisteredMimeType() const { return d->GetRegisteredMimeType(); } //////////// µS Registration & Properties ////////////// us::ServiceRegistration AbstractFileWriter::RegisterService(us::ModuleContext* context) { if (d->m_PrototypeFactory) return us::ServiceRegistration(); if(context == NULL) { context = us::GetModuleContext(); } d->RegisterMimeType(context); - if (this->GetMimeType().GetName().empty()) + if (this->GetMimeType()->GetName().empty()) { MITK_WARN << "Not registering writer due to empty MIME type."; return us::ServiceRegistration(); } struct PrototypeFactory : public us::PrototypeServiceFactory { AbstractFileWriter* const m_Prototype; PrototypeFactory(AbstractFileWriter* prototype) : m_Prototype(prototype) {} us::InterfaceMap GetService(us::Module* /*module*/, const us::ServiceRegistrationBase& /*registration*/) { return us::MakeInterfaceMap(m_Prototype->Clone()); } void UngetService(us::Module* /*module*/, const us::ServiceRegistrationBase& /*registration*/, const us::InterfaceMap& service) { delete us::ExtractInterface(service); } }; d->m_PrototypeFactory = new PrototypeFactory(this); us::ServiceProperties props = this->GetServiceProperties(); d->m_Reg = context->RegisterService(d->m_PrototypeFactory, props); return d->m_Reg; } void AbstractFileWriter::UnregisterService() { try { d->m_Reg.Unregister(); } catch (const std::exception&) {} } us::ServiceProperties AbstractFileWriter::GetServiceProperties() const { us::ServiceProperties result; result[IFileWriter::PROP_DESCRIPTION()] = this->GetDescription(); - result[IFileWriter::PROP_MIMETYPE()] = this->GetMimeType().GetName(); + result[IFileWriter::PROP_MIMETYPE()] = this->GetMimeType()->GetName(); result[IFileWriter::PROP_BASEDATA_TYPE()] = d->m_BaseDataType; result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking(); // for (IFileWriter::OptionList::const_iterator it = d->m_Options.begin(); it != d->m_Options.end(); ++it) // { // result[it->first] = std::string("true"); // } return result; } -CustomMimeType AbstractFileWriter::GetMimeType() const +const CustomMimeType* AbstractFileWriter::GetMimeType() const { return d->GetMimeType(); } void AbstractFileWriter::SetMimeTypePrefix(const std::string& prefix) { d->SetMimeTypePrefix(prefix); } std::string AbstractFileWriter::GetMimeTypePrefix() const { return d->GetMimeTypePrefix(); } us::ServiceRegistration AbstractFileWriter::RegisterMimeType(us::ModuleContext* context) { return d->RegisterMimeType(context); } void AbstractFileWriter::SetMimeType(const CustomMimeType& mimeType) { d->SetMimeType(mimeType); } void AbstractFileWriter::SetRanking(int ranking) { d->SetRanking(ranking); } //////////////////////// Options /////////////////////// void AbstractFileWriter::SetDefaultOptions(const IFileWriter::Options& defaultOptions) { d->SetDefaultOptions(defaultOptions); } IFileWriter::Options AbstractFileWriter::GetDefaultOptions() const { return d->GetDefaultOptions(); } IFileWriter::Options AbstractFileWriter::GetOptions() const { return d->GetOptions(); } us::Any AbstractFileWriter::GetOption(const std::string& name) const { return d->GetOption(name); } void AbstractFileWriter::SetOption(const std::string& name, const us::Any& value) { d->SetOption(name, value); } void AbstractFileWriter::SetOptions(const Options& options) { d->SetOptions(options); } ////////////////// MISC ////////////////// void AbstractFileWriter::AddProgressCallback(const ProgressCallback& callback) { d->AddProgressCallback(callback); } void AbstractFileWriter::RemoveProgressCallback(const ProgressCallback& callback) { d->RemoveProgressCallback(callback); } ////////////////// µS related Getters ////////////////// int AbstractFileWriter::GetRanking() const { return d->GetRanking(); } void AbstractFileWriter::SetBaseDataType(const std::string& baseDataType) { d->m_BaseDataType = baseDataType; } std::string AbstractFileWriter::GetDescription() const { return d->GetDescription(); } std::string AbstractFileWriter::GetBaseDataType() const { return d->m_BaseDataType; } void AbstractFileWriter::ValidateOutputLocation() const { if (this->GetOutputStream() == NULL) { // check if a file name is set and if we can write to it const std::string fileName = this->GetOutputLocation(); if (fileName.empty()) { mitkThrow() << "No output location or stream specified"; } } } void AbstractFileWriter::SetDescription(const std::string& description) { d->SetDescription(description); } } diff --git a/Core/Code/IO/mitkAbstractFileWriter.h b/Core/Code/IO/mitkAbstractFileWriter.h index a9620eff97..9cbcb55568 100644 --- a/Core/Code/IO/mitkAbstractFileWriter.h +++ b/Core/Code/IO/mitkAbstractFileWriter.h @@ -1,232 +1,232 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef AbstractFileWriter_H_HEADER_INCLUDED_C1E7E521 #define AbstractFileWriter_H_HEADER_INCLUDED_C1E7E521 // Macro #include // MITK #include #include // Microservices #include #include #include #include namespace us { struct PrototypeServiceFactory; } namespace mitk { class CustomMimeType; /** * @brief Base class for writing mitk::BaseData objects to files or streams. * * In general, all file writers should derive from this class, this way it is * made sure that the new implementation is * exposed to the Microservice-Framework and that is automatically available troughout MITK. * The default implementation only requires one Write() * method and the Clone() method to be implemented. * * @ingroup IO */ class MITK_CORE_EXPORT AbstractFileWriter : public mitk::IFileWriter { public: virtual void SetInput(const BaseData* data); virtual const BaseData* GetInput() const; virtual void SetOutputLocation(const std::string& location); virtual std::string GetOutputLocation() const; virtual void SetOutputStream(const std::string& location, std::ostream* os); virtual std::ostream* GetOutputStream() const; /** * \brief Write the base data to the specified location or output stream. * * This method must be implemented for each specific writer. Call * GetOutputStream() first and check for a non-null stream to write to. * If the output stream is \c NULL, use GetOutputLocation() to write * to a local file-system path. * * If the reader cannot use streams directly, use GetLocalFile() to retrieve * a temporary local file name instead. * * \throws mitk::Exception * * \see GetLocalFile() * \see IFileWriter::Write() */ virtual void Write() = 0; virtual ConfidenceLevel GetConfidenceLevel() const; MimeType GetRegisteredMimeType() const; virtual Options GetOptions() const; virtual us::Any GetOption(const std::string &name) const; virtual void SetOptions(const Options& options); virtual void SetOption(const std::string& name, const us::Any& value); virtual void AddProgressCallback(const ProgressCallback& callback); virtual void RemoveProgressCallback(const ProgressCallback& callback); us::ServiceRegistration RegisterService(us::ModuleContext* context = us::GetModuleContext()); void UnregisterService(); protected: /** * @brief A local file representation for streams. * * If a writer can only work with local files, use an instance * of this class to get either a temporary file name for writing * to the specified output stream or the original output location * if no output stream was set. */ class LocalFile { public: LocalFile(IFileWriter* writer); // Writes to the ostream and removes the temporary file ~LocalFile(); // Creates a temporary file for output operations. std::string GetFileName(); private: // disabled LocalFile(); LocalFile(const LocalFile&); LocalFile& operator=(const LocalFile& other); struct Impl; std::auto_ptr d; }; /** * @brief An output stream wrapper. * * If a writer can only work with output streams, use an instance * of this class to either wrap the specified output stream or * create a new output stream based on the output location in the * file system. */ class OutputStream : public std::ostream { public: OutputStream(IFileWriter* writer, std::ios_base::openmode mode = std::ios_base::trunc | std::ios_base::out); ~OutputStream(); private: std::ostream* m_Stream; }; ~AbstractFileWriter(); AbstractFileWriter(const AbstractFileWriter& other); AbstractFileWriter(const std::string& baseDataType); AbstractFileWriter(const std::string& baseDataType, const CustomMimeType& mimeType, const std::string& description); virtual us::ServiceProperties GetServiceProperties() const; /** * Registers a new CustomMimeType service object. * * This method is called from RegisterService and the default implementation * registers a new mime-type service object if all of the following conditions * are true: * * - TODO * * @param context * @return * @throws std::invalid_argument if \c context is NULL. */ virtual us::ServiceRegistration RegisterMimeType(us::ModuleContext* context); void SetMimeType(const CustomMimeType& mimeType); /** * @return Get the mime-type this writer can handle. */ - CustomMimeType GetMimeType() const; + const CustomMimeType* GetMimeType() const; void SetMimeTypePrefix(const std::string& prefix); std::string GetMimeTypePrefix() const; /** * \brief Sets a human readable description of this writer. * * This will be used in file dialogs for example. */ void SetDescription(const std::string& description); std::string GetDescription() const; void SetDefaultOptions(const Options& defaultOptions); Options GetDefaultOptions() const; /** * \brief Set the service ranking for this file writer. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which writer to use if several * equivalent writers have been found. * It may be used to replace a default writer from MITK in your own project. * E.g. if you want to use your own writer for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetRanking(int ranking); int GetRanking() const; /** * \brief Sets the name of the mitk::Basedata that this writer is able to handle. * * The correct value is the one given as the first parameter in the mitkNewMacro of that BaseData derivate. * You can also retrieve it by calling GetNameOfClass() on an instance of said data. */ void SetBaseDataType(const std::string& baseDataType); virtual std::string GetBaseDataType() const; void ValidateOutputLocation() const; private: AbstractFileWriter& operator=(const AbstractFileWriter& other); virtual mitk::IFileWriter* Clone() const = 0; class Impl; std::auto_ptr d; }; } // namespace mitk #endif /* AbstractFileWriter_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Core/Code/IO/mitkCustomMimeType.cpp b/Core/Code/IO/mitkCustomMimeType.cpp index b25349b78c..240570efdb 100644 --- a/Core/Code/IO/mitkCustomMimeType.cpp +++ b/Core/Code/IO/mitkCustomMimeType.cpp @@ -1,144 +1,165 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCustomMimeType.h" #include "mitkMimeType.h" #include namespace mitk { struct CustomMimeType::Impl { std::string m_Name; std::string m_Category; std::vector m_Extensions; std::string m_Comment; }; CustomMimeType::~CustomMimeType() { delete d; } CustomMimeType::CustomMimeType() : d(new Impl) { } CustomMimeType::CustomMimeType(const std::string& name) : d(new Impl) { d->m_Name = name; } CustomMimeType::CustomMimeType(const CustomMimeType& other) : d(new Impl(*other.d)) { } CustomMimeType::CustomMimeType(const MimeType& other) : d(new Impl) { d->m_Name = other.GetName(); d->m_Category = other.GetCategory(); d->m_Extensions = other.GetExtensions(); d->m_Comment = other.GetComment(); } CustomMimeType& CustomMimeType::operator=(const CustomMimeType& other) { CustomMimeType tmp(other); Swap(tmp); return *this; } CustomMimeType&CustomMimeType::operator=(const MimeType& other) { CustomMimeType tmp(other); Swap(tmp); return *this; } std::string CustomMimeType::GetName() const { return d->m_Name; } std::string CustomMimeType::GetCategory() const { return d->m_Category; } std::vector CustomMimeType::GetExtensions() const { return d->m_Extensions; } std::string CustomMimeType::GetComment() const { if (!d->m_Comment.empty()) return d->m_Comment; if (!d->m_Extensions.empty()) { return d->m_Extensions.front() + " File"; } return "Unknown"; } +bool CustomMimeType::AppliesTo(const std::string& path) const +{ + for (std::vector::const_iterator iter = d->m_Extensions.begin(), + iterEnd = d->m_Extensions.end(); iter != iterEnd; ++iter) + { + if (!iter->empty() && path.size() >= iter->size()) + { + if (path.substr(path.size() - iter->size()) == *iter) + { + return true; + } + } + } + return false; +} + void CustomMimeType::SetName(const std::string& name) { d->m_Name = name; } void CustomMimeType::SetCategory(const std::string& category) { d->m_Category = category; } void CustomMimeType::SetExtension(const std::string& extension) { d->m_Extensions.clear(); d->m_Extensions.push_back(extension); } void CustomMimeType::AddExtension(const std::string& extension) { if (std::find(d->m_Extensions.begin(), d->m_Extensions.end(), extension) == d->m_Extensions.end()) { d->m_Extensions.push_back(extension); } } void CustomMimeType::SetComment(const std::string& comment) { d->m_Comment = comment; } void CustomMimeType::Swap(CustomMimeType& r) { Impl* d1 = d; d = r.d; r.d = d1; } +CustomMimeType* CustomMimeType::Clone() const +{ + return new CustomMimeType(*this); +} + void swap(CustomMimeType& l, CustomMimeType& r) { l.Swap(r); } } diff --git a/Core/Code/IO/mitkCustomMimeType.h b/Core/Code/IO/mitkCustomMimeType.h index b232c58696..46a6a25a2f 100644 --- a/Core/Code/IO/mitkCustomMimeType.h +++ b/Core/Code/IO/mitkCustomMimeType.h @@ -1,70 +1,74 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCUSTOMMIMETYPE_H #define MITKCUSTOMMIMETYPE_H #include -#include +#include #include #include namespace mitk { class MimeType; class MITK_CORE_EXPORT CustomMimeType { public: CustomMimeType(); CustomMimeType(const std::string& name); CustomMimeType(const CustomMimeType& other); explicit CustomMimeType(const MimeType& other); - ~CustomMimeType(); + virtual ~CustomMimeType(); CustomMimeType& operator=(const CustomMimeType& other); CustomMimeType& operator=(const MimeType& other); std::string GetName() const; std::string GetCategory() const; std::vector GetExtensions() const; std::string GetComment() const; + virtual bool AppliesTo(const std::string& path) const; + void SetName(const std::string& name); void SetCategory(const std::string& category); void SetExtension(const std::string& extension); void AddExtension(const std::string& extension); void SetComment(const std::string& comment); void Swap(CustomMimeType& r); + virtual CustomMimeType* Clone() const; + private: struct Impl; Impl* d; }; void swap(CustomMimeType& l, CustomMimeType& r); } -US_DECLARE_SERVICE_INTERFACE(mitk::CustomMimeType, "org.mitk.CustomMimeType") +MITK_DECLARE_SERVICE_INTERFACE(mitk::CustomMimeType, "org.mitk.CustomMimeType") #endif // MITKCUSTOMMIMETYPE_H diff --git a/Core/Code/IO/mitkDicomSeriesReader.cpp b/Core/Code/IO/mitkDicomSeriesReader.cpp index fec7e5ef43..da388ffcd2 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.cpp +++ b/Core/Code/IO/mitkDicomSeriesReader.cpp @@ -1,1781 +1,1777 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { - std::string DicomSeriesReader::ReaderImplementationLevelToString( const ReaderImplementationLevel& enumValue ) { switch (enumValue) { case ReaderImplementationLevel_Supported: return "Supported"; case ReaderImplementationLevel_PartlySupported: return "PartlySupported"; case ReaderImplementationLevel_Implemented: return "Implemented"; case ReaderImplementationLevel_Unsupported: return "Unsupported"; default: return ""; }; } std::string DicomSeriesReader::PixelSpacingInterpretationToString( const PixelSpacingInterpretation& enumValue ) { switch (enumValue) { case PixelSpacingInterpretation_SpacingInPatient: return "In Patient"; case PixelSpacingInterpretation_SpacingAtDetector: return "At Detector"; case PixelSpacingInterpretation_SpacingUnknown: return "Unknown spacing"; default: return ""; }; } const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; // Image Pixel module dictionary["0028|0004"] = "dicom.pixel.PhotometricInterpretation"; dictionary["0028|0010"] = "dicom.pixel.Rows"; dictionary["0028|0011"] = "dicom.pixel.Columns"; // Image Plane module dictionary["0028|0030"] = "dicom.PixelSpacing"; dictionary["0018|1164"] = "dicom.ImagerPixelSpacing"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback, preLoadedImageBlock)) { if( filenames.empty() ) { return NULL; } return node; } else { return NULL; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, itk::SmartPointer preLoadedImageBlock) { if( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(NULL); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR || io->GetPixelType() == itk::ImageIOBase::RGB) { LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); } if (node.GetData()) { return true; } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, itk::SmartPointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // axial gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } std::string DicomSeriesReader::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } bool DicomSeriesReader::DICOMStringToSpacing(const std::string& s, ScalarType& spacingX, ScalarType& spacingY) { bool successful = false; std::istringstream spacingReader(s); std::string spacing; if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingY = atof( spacing.c_str() ); if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingX = atof( spacing.c_str() ); successful = true; } } return successful; } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( originReader, coordinate, '\\' ) && dim < 3) { p[dim++]= atof(coordinate.c_str()); } if (dim && dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } else if (dim == 0) { successful = false; p.Fill(0.0); // assume default (0,0,0) } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 ) { if (dim<3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim && dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } else if (dim == 0) { // fill with defaults right.Fill(0.0); right[0] = 1.0; up.Fill(0.0); up[1] = 1.0; successful = false; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (StringContainer::const_iterator fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] ); if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position if ( result.GetBlockFilenames().empty() ) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock( *fileIter ); StringContainer remainingFiles; remainingFiles.insert( remainingFiles.end(), fileIter+1, files.end() ); result.AddFilesToUnsortedBlock( remainingFiles ); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { // check if this is at least roughly the same angle as recorded in DICOM tags if ( tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end() ) { // read value, compare to calculated angle std::string tiltStr = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagGantryTilt] ); double angle = atof(tiltStr.c_str()); MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees(); // TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing serious) // TODO TODO TODO when angle -27 and tiltangle 63, this will never trigger the if-clause... useless check in this case! old bug..?! if ( fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25) { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this is fine { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we are right) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if ( result.GetBlockFilenames().size() == 2 ) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID scanner.AddTag( tagSOPClassUID ); const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing scanner.AddTag( tagImagerPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt scanner.AddTag( tagGantryTilt ); const gdcm::Tag tagModality(0x0008, 0x0060); // modality scanner.AddTag( tagModality ); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag( tagNumberOfFrames ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments (when anybody asks for it) FileNamesGrouping result; // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return result; } // assign files IDs that will separate them for loading into image blocks for (gdcm::Scanner::ConstIterator fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue; /* sort out multi-frame if ( scanner.GetValue( fileIter->first , tagNumberOfFrames ) ) { MITK_INFO << "Ignoring " << fileIter->first << " because we cannot handle multi-frame images."; continue; } */ // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); result[ moreUniqueSeriesId ].AddFile( fileIter->first ); } // PART II: sort slices spatially (or at least consistently if this is NOT possible, see method) for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { try { result[ groupIter->first ] = ImageBlockDescriptor( SortSeriesSlices( groupIter->second.GetFilenames() ) ); // sort each slice group spatially } catch(...) { MITK_ERROR << "Caught something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks // * this includes images which DO NOT PROVIDE spatial information, i.e. all images w/o ImagePositionPatient will be loaded separately FileNamesGrouping groupsOf3DPlusTBlocks; // final result of this function for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { FileNamesGrouping groupsOf3DBlocks; // intermediate result for only this group(!) std::map mapOf3DBlockAnalysisResults; StringContainer filesStillToAnalyze = groupIter->second.GetFilenames(); std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID << " of " << groupIter->second.GetFilenames().size() << " files"; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; ImageBlockDescriptor thisBlock( analysisResult.GetBlockFilenames() ); std::string firstFileInBlock = thisBlock.GetFilenames().front(); thisBlock.SetImageBlockUID( newGroupUID.str() ); thisBlock.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSeriesInstanceUID ) ) ); thisBlock.SetHasGantryTiltCorrected( analysisResult.ContainsGantryTilt() ); thisBlock.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSOPClassUID ) ) ); thisBlock.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagNumberOfFrames ) ) ); thisBlock.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagModality ) ) ); thisBlock.SetPixelSpacingInformation( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagPixelSpacing ) ), DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagImagerPixelSpacing ) ) ); thisBlock.SetHasMultipleTimePoints( false ); groupsOf3DBlocks[ newGroupUID.str() ] = thisBlock; //MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; MITK_DEBUG << "Result: sorted 3D group with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; StringContainer debugOutputFiles = analysisResult.GetBlockFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " IN " << *siter; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis for (StringContainer::const_iterator siter = filesStillToAnalyze.begin(); siter != filesStillToAnalyze.end(); ++siter) MITK_DEBUG << " OUT " << *siter; - } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of groupsOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output groupsOf3DPlusTBlocks.insert( groupsOf3DBlocks.begin(), groupsOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( FileNamesGrouping::const_iterator block3DIter = groupsOf3DBlocks.begin(); block3DIter != groupsOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.GetFilenames().size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; MITK_DEBUG << " 3D+t group " << thisBlockKey; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *previous_origin_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *destination_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ), *previous_destination_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString( origin_value ); std::string previousOriginString = ConstCharStarToString( previous_origin_value ); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString( destination_value ); std::string previousDestinationString = ConstCharStarToString( previous_destination_value ); identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); } - } catch(...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block groupsOf3DPlusTBlocks[previousBlockKey].AddFiles( block3DIter->second.GetFilenames() ); groupsOf3DPlusTBlocks[previousBlockKey].SetHasMultipleTimePoints(true); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; int numberOfTimeSteps = groupsOf3DPlusTBlocks[previousBlockKey].GetFilenames().size() / numberOfFilesInPreviousBlock; MITK_DEBUG << " ==> group closed with " << numberOfTimeSteps << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( FileNamesGrouping::const_iterator groupIter = groupsOf3DPlusTBlocks.begin(); groupIter != groupsOf3DPlusTBlocks.end(); ++groupIter ) { ImageBlockDescriptor block = groupIter->second; MITK_DEBUG << " " << block.GetFilenames().size() << " '" << block.GetModality() << "' images (" << block.GetSOPClassUIDAsString() << ") in volume " << block.GetImageBlockUID(); MITK_DEBUG << " (gantry tilt : " << (block.HasGantryTiltCorrected()?"Yes":"No") << "; " "pixel spacing : " << PixelSpacingInterpretationToString( block.GetPixelSpacingType() ) << "; " "3D+t: " << (block.HasMultipleTimePoints()?"Yes":"No") << "; " "reader support: " << ReaderImplementationLevelToString( block.GetReaderImplementationLevel() ) << ")"; StringContainer debugOutputFiles = block.GetFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " F " << *siter; } MITK_DEBUG << "================================================================================"; return groupsOf3DPlusTBlocks; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception&) { // we are happy with even nothing, this will just group images of a series //MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImagerPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfFrames ); // be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); if (tagValueMap.find(tagImageOrientation) != tagValueMap.end()) { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError ); //string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue( simplifiedOrientationString ); } constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { FileNamesGrouping allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for ( FileNamesGrouping::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { return idIter->second.GetFilenames(); } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns (each piece of information except the rows/columns might be missing) sorting with GdcmSortFunction tries its best by sorting by spatial position and more hints (acquisition number, acquisition time, trigger time) but will always produce a sorting by falling back to SOP Instance UID. */ gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { if (sorter.Sort(unsortedFilenames)) { return sorter.GetFilenames(); } else { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } catch(std::logic_error&) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // This method MUST accept missing location and position information (and all else, too) // because we cannot rely on anything // (restriction on the sentence before: we have to provide consistent sorting, so we // rely on the minimum information all DICOM files need to provide: SOP Instance UID) /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns */ static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) static const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation // see if we have Image Position and Orientation if ( ds1.FindDataElement(tagImagePositionPatient) && ds1.FindDataElement(tagImageOrientation) && ds2.FindDataElement(tagImagePositionPatient) && ds2.FindDataElement(tagImageOrientation) ) { gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 ) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } // if we can sort by just comparing the distance, we do exactly that if ( fabs(dist1 - dist2) >= mitk::eps) { // default: compare position return dist1 < dist2; } else // we need to check more properties to distinguish slices { // try to sort by Acquisition Number static const gdcm::Tag tagAcquisitionNumber(0x0020, 0x0012); if (ds1.FindDataElement(tagAcquisitionNumber) && ds2.FindDataElement(tagAcquisitionNumber)) { gdcm::Attribute<0x0020,0x0012> acquisition_number1; // Acquisition number gdcm::Attribute<0x0020,0x0012> acquisition_number2; acquisition_number1.Set(ds1); acquisition_number2.Set(ds2); if (acquisition_number1 != acquisition_number2) { return acquisition_number1 < acquisition_number2; } else // neither position nor acquisition number are good for sorting, so check more { // try to sort by Acquisition Time static const gdcm::Tag tagAcquisitionTime(0x0008, 0x0032); if (ds1.FindDataElement(tagAcquisitionTime) && ds2.FindDataElement(tagAcquisitionTime)) { gdcm::Attribute<0x0008,0x0032> acquisition_time1; // Acquisition time gdcm::Attribute<0x0008,0x0032> acquisition_time2; acquisition_time1.Set(ds1); acquisition_time2.Set(ds2); if (acquisition_time1 != acquisition_time2) { return acquisition_time1 < acquisition_time2; } else // we gave up on image position, acquisition number and acquisition time now { // let's try trigger time static const gdcm::Tag tagTriggerTime(0x0018, 0x1060); if (ds1.FindDataElement(tagTriggerTime) && ds2.FindDataElement(tagTriggerTime)) { gdcm::Attribute<0x0018,0x1060> trigger_time1; // Trigger time gdcm::Attribute<0x0018,0x1060> trigger_time2; trigger_time1.Set(ds1); trigger_time2.Set(ds2); if (trigger_time1 != trigger_time2) { return trigger_time1 < trigger_time2; } // ELSE! // for this and many previous ifs we fall through if nothing lets us sort } // . } // . } // . } } } } // . // LAST RESORT: all valuable information for sorting is missing. // Sort by some meaningless but unique identifiers to satisfy the sort function static const gdcm::Tag tagSOPInstanceUID(0x0008, 0x0018); if (ds1.FindDataElement(tagSOPInstanceUID) && ds2.FindDataElement(tagSOPInstanceUID)) { MITK_DEBUG << "Dicom images are missing attributes for a meaningful sorting, falling back to SOP instance UID comparison."; gdcm::Attribute<0x0008,0x0018> SOPInstanceUID1; // SOP instance UID is mandatory and unique gdcm::Attribute<0x0008,0x0018> SOPInstanceUID2; SOPInstanceUID1.Set(ds1); SOPInstanceUID2.Set(ds2); return SOPInstanceUID1 < SOPInstanceUID2; } else { // no DICOM file should really come down here, this should only be reached with unskillful and unlucky manipulation of files std::string error_message("Malformed DICOM images, which do not even contain a SOP Instance UID."); MITK_ERROR << error_message; throw std::logic_error( error_message ); } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor& blockInfo, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image); } void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); //DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for ( std::list::iterator i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ ) { - const StringContainer& files = (*i); unsigned int slice(0); for ( StringContainer::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); //If more than one time step add postfix ".t" + timestep if(timeStep != 0) { std::ostringstream postfix; postfix << ".t" << timeStep; propertyKeySliceLocation.append(postfix.str()); propertyKeyInstanceNumber.append(postfix.str()); propertyKeySOPInstanceNumber.append(postfix.str()); } image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) ); image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) ); image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) ); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary(); const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); itk::MetaDataDictionary::ConstIterator dictIter = dict.Begin(); while ( dictIter != dict.End() ) { //MITK_DEBUG << "Key " << dictIter->first; std::string value; if ( itk::ExposeMetaData( dict, dictIter->first, value ) ) { //MITK_DEBUG << "Value " << value; TagToPropertyMapType::const_iterator valuePosition = propertyLookup.find( dictIter->first ); if ( valuePosition != propertyLookup.end() ) { std::string propertyKey = valuePosition->second; //MITK_DEBUG << "--> " << propertyKey; image->SetProperty( propertyKey.c_str(), StringProperty::New(value) ); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ; } ++dictIter; } // copy imageblockdescriptor as properties image->SetProperty("dicomseriesreader.SOPClass", StringProperty::New(blockInfo.GetSOPClassUIDAsString())); image->SetProperty("dicomseriesreader.ReaderImplementationLevelString", StringProperty::New(ReaderImplementationLevelToString( blockInfo.GetReaderImplementationLevel() ))); image->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( blockInfo.GetReaderImplementationLevel() )); image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New(PixelSpacingInterpretationToString( blockInfo.GetPixelSpacingType() ))); image->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New(blockInfo.GetPixelSpacingType())); image->SetProperty("dicomseriesreader.MultiFrameImage", BoolProperty::New(blockInfo.IsMultiFrameImage())); image->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New(blockInfo.HasGantryTiltCorrected())); image->SetProperty("dicomseriesreader.3D+t", BoolProperty::New(blockInfo.HasMultipleTimePoints())); } void DicomSeriesReader::FixSpacingInformation( mitk::Image* image, const ImageBlockDescriptor& imageBlockDescriptor ) { // spacing provided by ITK/GDCM Vector3D imageSpacing = image->GetGeometry()->GetSpacing(); ScalarType imageSpacingX = imageSpacing[0]; ScalarType imageSpacingY = imageSpacing[1]; // spacing as desired by MITK (preference for "in patient", else "on detector", or "1.0/1.0") ScalarType desiredSpacingX = imageSpacingX; ScalarType desiredSpacingY = imageSpacingY; imageBlockDescriptor.GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); MITK_DEBUG << "Loaded spacing: " << imageSpacingX << "/" << imageSpacingY; MITK_DEBUG << "Corrected spacing: " << desiredSpacingX << "/" << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; image->GetGeometry()->SetSpacing( imageSpacing ); } void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { const char* previousCLocale = setlocale(LC_NUMERIC, NULL); setlocale(LC_NUMERIC, "C"); std::locale previousCppLocale( std::cin.getloc() ); std::locale l( "C" ); std::cin.imbue(l); ImageBlockDescriptor imageBlockDescriptor; const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID const gdcm::Tag tagModality(0x0008, 0x0060); // modality const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames try { Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock; CallbackCommand *command = callback ? new CallbackCommand(callback) : 0; bool initialize_node = false; /* special case for Philips 3D+t ultrasound images */ if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) ) { // TODO what about imageBlockDescriptor? // TODO what about preLoadedImageBlock? ReadPhilips3DDicom(filenames.front().c_str(), image); initialize_node = true; } else { /* default case: assume "normal" image blocks, possibly 3D+t */ bool canLoadAs4D(true); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); // need non-const access for map gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D ); unsigned int volume_count = imageBlocks.size(); imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSeriesInstanceUID ) ) ); imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSOPClassUID ) ) ); imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagModality ) ) ); imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagNumberOfFrames ) ) ); imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagPixelSpacing ) ), ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagImagerPixelSpacing ) ) ); GantryTiltInformation tiltInfo; // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) { // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents // check first and last position slice from tags, make some calculations to detect tilt std::string firstFilename(imageBlocks.front().front()); // calculate from first and last slice to minimize rounding errors std::string secondFilename(imageBlocks.front().back()); std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) ); std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) ); std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) ); bool ignoredConversionError(-42); // hard to get here, no graceful way to react Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) ); Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) ); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError ); tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 ); correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); } else { correctTilt = false; // we CANNOT do that } imageBlockDescriptor.SetHasGantryTiltCorrected( correctTilt ); if (volume_count == 1 || !canLoadAs4D || !load4D) { - DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock ); // load first 3D block imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded imageBlockDescriptor.SetHasMultipleTimePoints( false ); FixSpacingInformation( image, imageBlockDescriptor ); CopyMetaDataToImageProperties( imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image); initialize_node = true; } else if (volume_count > 1) { imageBlockDescriptor.AddFiles(filenames); // all is loaded imageBlockDescriptor.SetHasMultipleTimePoints( true ); DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock ); initialize_node = true; } - } if (initialize_node) { // forward some image properties to node node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true ); + std::string patientName = node.GetProperty("dicom.patient.PatientsName")->GetValueAsString(); + node.SetData( image ); + node.SetName(patientName); setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); } MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):"; MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0]; MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); MITK_DEBUG << "--------------------------------------------------------------------------------"; } catch (std::exception& e) { // reset locale then throw up setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom"; throw e; } } void DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner) { const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) scanner.AddTag(tagImagePositionPatient); const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID scanner.AddTag(tagSeriesInstanceUID); const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation scanner.AddTag(tagImageOrientation); const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag( tagSliceLocation ); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag( tagInstanceNumber ); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag( tagSOPInstanceNumber ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing scanner.AddTag( tagImagerPixelSpacing ); const gdcm::Tag tagModality(0x0008, 0x0060); // Modality scanner.AddTag( tagModality ); const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID scanner.AddTag( tagSOPClassUID ); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag( tagNumberOfFrames ); scanner.Scan(filenames); // make available image information for each file } std::list DicomSeriesReader::SortIntoBlocksFor3DplusT( const StringContainer& presortedFilenames, const gdcm::Scanner::MappingType& tagValueMappings, bool /*sort*/, bool& canLoadAs4D ) { std::list imageBlocks; // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); std::string firstPosition; unsigned int numberOfBlocks(0); // number of 3D image blocks static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) // loop files to determine number of image blocks for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); fileIter != sorted_filenames.end(); ++fileIter) { gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second; if(tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end()) { // we expect to get images w/ missing position information ONLY as separated blocks. assert( presortedFilenames.size() == 1 ); numberOfBlocks = 1; break; } std::string position = tagToValueMap.find(tagImagePositionPatient)->second; MITK_DEBUG << " " << *fileIter << " at " << position; if (firstPosition.empty()) { firstPosition = position; } if ( position == firstPosition ) { ++numberOfBlocks; } else { break; // enough information to know the number of image blocks } } MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files // loop files to sort them into image blocks unsigned int numberOfExpectedSlices(0); for (unsigned int block = 0; block < numberOfBlocks; ++block) { StringContainer filesOfCurrentBlock; for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; fileIter != sorted_filenames.end(); //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts ) { filesOfCurrentBlock.push_back( *fileIter ); for (unsigned int b = 0; b < numberOfBlocks; ++b) { if (fileIter != sorted_filenames.end()) ++fileIter; } } imageBlocks.push_back(filesOfCurrentBlock); if (block == 0) { numberOfExpectedSlices = filesOfCurrentBlock.size(); } else { if (filesOfCurrentBlock.size() != numberOfExpectedSlices) { MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks << " image blocks of " << numberOfExpectedSlices << " images each. Block " << block << " got " << filesOfCurrentBlock.size() << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible) canLoadAs4D = false; } } } return imageBlocks; } Image::Pointer DicomSeriesReader ::MultiplexLoadDICOMByITK(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else { return NULL; } } Image::Pointer DicomSeriesReader ::MultiplexLoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(imageBlocks.front().front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITK4DScalar(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITK4DRGBPixel(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else { return NULL; } } - -} // end namespace mitk +} // end namespace mitk \ No newline at end of file diff --git a/Core/Code/IO/mitkFileReaderRegistry.cpp b/Core/Code/IO/mitkFileReaderRegistry.cpp index 682e6237f2..11805052d7 100644 --- a/Core/Code/IO/mitkFileReaderRegistry.cpp +++ b/Core/Code/IO/mitkFileReaderRegistry.cpp @@ -1,114 +1,114 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFileReaderRegistry.h" #include "mitkIMimeTypeProvider.h" #include "mitkCoreServices.h" // Microservices #include #include #include #include #include "itksys/SystemTools.hxx" mitk::FileReaderRegistry::FileReaderRegistry() { } mitk::FileReaderRegistry::~FileReaderRegistry() { for (std::map >::iterator iter = m_ServiceObjects.begin(), end = m_ServiceObjects.end(); iter != end; ++iter) { iter->second.UngetService(iter->first); } } -mitk::MimeType mitk::FileReaderRegistry::GetMimeTypeForExtension(const std::string& extension, us::ModuleContext* context) +mitk::MimeType mitk::FileReaderRegistry::GetMimeTypeForFile(const std::string& path, us::ModuleContext* context) { mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider(context)); - std::vector mimeTypes = mimeTypeProvider->GetMimeTypesForExtension(extension); + std::vector mimeTypes = mimeTypeProvider->GetMimeTypesForFile(path); if (mimeTypes.empty()) { return MimeType(); } else { return mimeTypes.front(); } } std::vector mitk::FileReaderRegistry::GetReferences(const MimeType& mimeType, us::ModuleContext* context) { std::string filter = us::LDAPProp(us::ServiceConstants::OBJECTCLASS()) == us_service_interface_iid() && us::LDAPProp(IFileReader::PROP_MIMETYPE()) == mimeType.GetName(); return context->GetServiceReferences(filter); } mitk::IFileReader* mitk::FileReaderRegistry::GetReader(const mitk::FileReaderRegistry::ReaderReference& ref, us::ModuleContext* context) { us::ServiceObjects serviceObjects = context->GetServiceObjects(ref); mitk::IFileReader* reader = serviceObjects.GetService(); m_ServiceObjects.insert(std::make_pair(reader, serviceObjects)); return reader; } std::vector mitk::FileReaderRegistry::GetReaders(const MimeType& mimeType, us::ModuleContext* context ) { std::vector result; if (!mimeType.IsValid()) return result; std::vector > refs = GetReferences(mimeType, context); std::sort(refs.begin(), refs.end()); result.reserve(refs.size()); // Translate List of ServiceRefs to List of Pointers for (std::vector >::const_reverse_iterator iter = refs.rbegin(), end = refs.rend(); iter != end; ++iter) { us::ServiceObjects serviceObjects = context->GetServiceObjects(*iter); mitk::IFileReader* reader = serviceObjects.GetService(); m_ServiceObjects.insert(std::make_pair(reader, serviceObjects)); result.push_back(reader); } return result; } void mitk::FileReaderRegistry::UngetReader(mitk::IFileReader* reader) { std::map >::iterator readerIter = m_ServiceObjects.find(reader); if (readerIter != m_ServiceObjects.end()) { readerIter->second.UngetService(reader); m_ServiceObjects.erase(readerIter); } } void mitk::FileReaderRegistry::UngetReaders(const std::vector& readers) { for (std::vector::const_iterator iter = readers.begin(), end = readers.end(); iter != end; ++iter) { this->UngetReader(*iter); } } diff --git a/Core/Code/IO/mitkFileReaderRegistry.h b/Core/Code/IO/mitkFileReaderRegistry.h index b0abe285cb..6cb13d2f85 100644 --- a/Core/Code/IO/mitkFileReaderRegistry.h +++ b/Core/Code/IO/mitkFileReaderRegistry.h @@ -1,85 +1,85 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef FileReaderRegistry_H_HEADER_INCLUDED_C1E7E521 #define FileReaderRegistry_H_HEADER_INCLUDED_C1E7E521 #include #include #include // Microservices #include #include #include namespace mitk { class MimeType; /** * @ingroup IO * * Provides convenient access to mitk::IFileReader instances and reading * files into mitk::BaseData types. * * \note The life-time of all mitk::IFileReader objects returned by an * instance of this class ends with the destruction of that instance. */ class MITK_CORE_EXPORT FileReaderRegistry { public: typedef us::ServiceReference ReaderReference; FileReaderRegistry(); ~FileReaderRegistry(); /** - * @brief Get the highest ranked mime-type for the given extension. + * @brief Get the highest ranked mime-type for the given file name. * @param extension A file name extension without a leading dot. * @param context * @return The highest ranked mime-type containing \c extension in * its extension list. */ - static MimeType GetMimeTypeForExtension(const std::string& extension, us::ModuleContext* context = us::GetModuleContext()); + static MimeType GetMimeTypeForFile(const std::string& path, us::ModuleContext* context = us::GetModuleContext()); static std::vector GetReferences(const MimeType& mimeType, us::ModuleContext* context = us::GetModuleContext()); mitk::IFileReader* GetReader(const ReaderReference& ref, us::ModuleContext* context = us::GetModuleContext()); std::vector GetReaders(const MimeType& mimeType, us::ModuleContext* context = us::GetModuleContext()); void UngetReader(mitk::IFileReader* reader); void UngetReaders(const std::vector& readers); private: // purposely not implemented FileReaderRegistry(const FileReaderRegistry&); FileReaderRegistry& operator=(const FileReaderRegistry&); std::map > m_ServiceObjects; }; } // namespace mitk #endif /* FileReaderRegistry_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Core/Code/IO/mitkFileWriterSelector.cpp b/Core/Code/IO/mitkFileWriterSelector.cpp index 86ff419f0b..77e59bf989 100644 --- a/Core/Code/IO/mitkFileWriterSelector.cpp +++ b/Core/Code/IO/mitkFileWriterSelector.cpp @@ -1,360 +1,356 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFileWriterSelector.h" #include #include #include #include #include #include #include #include #include #include #include namespace mitk { struct FileWriterSelector::Item::Impl : us::SharedData { Impl() : m_FileWriter(NULL) , m_ConfidenceLevel(IFileWriter::Unsupported) , m_BaseDataIndex(0) , m_Id(-1) {} us::ServiceReference m_FileWriterRef; IFileWriter* m_FileWriter; IFileWriter::ConfidenceLevel m_ConfidenceLevel; std::size_t m_BaseDataIndex; MimeType m_MimeType; long m_Id; }; struct FileWriterSelector::Impl : us::SharedData { Impl() : m_BestId(-1) , m_SelectedId(m_BestId) {} Impl(const Impl& other) : us::SharedData(other) , m_BestId(-1) , m_SelectedId(m_BestId) {} FileWriterRegistry m_WriterRegistry; std::map m_Items; std::set m_MimeTypes; long m_BestId; long m_SelectedId; }; FileWriterSelector::FileWriterSelector(const FileWriterSelector& other) : m_Data(other.m_Data) { } FileWriterSelector::FileWriterSelector(const BaseData* baseData, const std::string& mimeType, const std::string& path) : m_Data(new Impl) { mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::string destMimeType = mimeType; if (destMimeType.empty() && !path.empty()) { - // try to derive a mime-type from the extension - std::string ext = itksys::SystemTools::GetFilenameExtension(path); - if (ext.size() > 1) + // try to derive a mime-type from the file + std::vector mimeTypes = mimeTypeProvider->GetMimeTypesForFile(path); + if (!mimeTypes.empty()) { - std::vector mimeTypes = mimeTypeProvider->GetMimeTypesForExtension(ext.substr(1)); - if (!mimeTypes.empty()) - { - destMimeType = mimeTypes.front().GetName(); - } - else - { - // if both mimeType is empty and path does not contain a known - // extension, we stop here - return; - } + destMimeType = mimeTypes.front().GetName(); + } + else if (!itksys::SystemTools::GetFilenameExtension(path).empty()) + { + // If there are no suitable mime-type for the file AND an extension + // was supplied, we stop here. + return; } } std::vector classHierarchy = baseData->GetClassHierarchy(); // Get all writers and their mime types for the given base data type std::vector refs = m_Data->m_WriterRegistry.GetReferences(baseData, destMimeType); Item bestItem; for (std::vector::const_iterator iter = refs.begin(), iterEnd = refs.end(); iter != iterEnd; ++iter) { std::string mimeTypeName = iter->GetProperty(IFileWriter::PROP_MIMETYPE()).ToString(); if (!mimeTypeName.empty()) { MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); if (mimeType.IsValid()) { // There is a registered mime-type for this writer. Now get the confidence level // of this writer for writing the given base data object. IFileWriter* writer = m_Data->m_WriterRegistry.GetWriter(*iter); if (writer == NULL) continue; try { writer->SetInput(baseData); IFileWriter::ConfidenceLevel confidenceLevel = writer->GetConfidenceLevel(); if (confidenceLevel == IFileWriter::Unsupported) { continue; } std::string baseDataType = iter->GetProperty(IFileWriter::PROP_BASEDATA_TYPE()).ToString(); std::vector::iterator idxIter = std::find(classHierarchy.begin(), classHierarchy.end(), baseDataType); std::size_t baseDataIndex = std::numeric_limits::max(); if (idxIter != classHierarchy.end()) { baseDataIndex = std::distance(classHierarchy.begin(), idxIter); } Item item; item.d->m_FileWriterRef = *iter; item.d->m_FileWriter = writer; item.d->m_ConfidenceLevel = confidenceLevel; item.d->m_BaseDataIndex = baseDataIndex; item.d->m_MimeType = mimeType; item.d->m_Id = us::any_cast(iter->GetProperty(us::ServiceConstants::SERVICE_ID())); m_Data->m_Items.insert(std::make_pair(item.d->m_Id, item)); m_Data->m_MimeTypes.insert(mimeType); if (!bestItem.GetReference() || bestItem < item) { bestItem = item; } } catch (const us::BadAnyCastException& e) { MITK_WARN << "Unexpected: " << e.what(); } catch (const std::exception& e) { // Log the error but continue MITK_WARN << "IFileWriter::GetConfidenceLevel exception: " << e.what(); } } } } if (bestItem.GetReference()) { m_Data->m_BestId = bestItem.GetServiceId(); m_Data->m_SelectedId = m_Data->m_BestId; } } FileWriterSelector::~FileWriterSelector() { } FileWriterSelector& FileWriterSelector::operator=(const FileWriterSelector& other) { m_Data = other.m_Data; return *this; } bool FileWriterSelector::IsEmpty() const { return m_Data->m_Items.empty(); } std::vector FileWriterSelector::Get(const std::string& mimeType) const { std::vector result; for (std::map::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end(); iter != iterEnd; ++iter) { if (mimeType.empty() || iter->second.GetMimeType().GetName() == mimeType) { result.push_back(iter->second); } } std::sort(result.begin(), result.end()); return result; } std::vector FileWriterSelector::Get() const { return Get(this->GetSelected().d->m_MimeType.GetName()); } FileWriterSelector::Item FileWriterSelector::Get(long id) const { std::map::const_iterator iter = m_Data->m_Items.find(id); if (iter != m_Data->m_Items.end()) { return iter->second; } return Item(); } FileWriterSelector::Item FileWriterSelector::GetDefault() const { return Get(m_Data->m_BestId); } long FileWriterSelector::GetDefaultId() const { return m_Data->m_BestId; } FileWriterSelector::Item FileWriterSelector::GetSelected() const { return Get(m_Data->m_SelectedId); } long FileWriterSelector::GetSelectedId() const { return m_Data->m_SelectedId; } bool FileWriterSelector::Select(const std::string& mimeType) { std::vector items = Get(mimeType); if (items.empty()) return false; return Select(items.back()); } bool FileWriterSelector::Select(const FileWriterSelector::Item& item) { return Select(item.d->m_Id); } bool FileWriterSelector::Select(long id) { if (id > -1) { if (m_Data->m_Items.find(id) == m_Data->m_Items.end()) { return false; } m_Data->m_SelectedId = id; return true; } return false; } std::vector FileWriterSelector::GetMimeTypes() const { std::vector result; result.reserve(m_Data->m_MimeTypes.size()); result.assign(m_Data->m_MimeTypes.begin(), m_Data->m_MimeTypes.end()); return result; } void FileWriterSelector::Swap(FileWriterSelector& fws) { m_Data.Swap(fws.m_Data); } FileWriterSelector::Item::Item(const FileWriterSelector::Item& other) : d(other.d) { } FileWriterSelector::Item::~Item() { } FileWriterSelector::Item& FileWriterSelector::Item::operator=(const FileWriterSelector::Item& other) { d = other.d; return *this; } IFileWriter* FileWriterSelector::Item::GetWriter() const { return d->m_FileWriter; } std::string FileWriterSelector::Item::GetDescription() const { us::Any descr = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_DESCRIPTION()); if (descr.Empty()) return std::string(); return descr.ToString(); } IFileWriter::ConfidenceLevel FileWriterSelector::Item::GetConfidenceLevel() const { return d->m_ConfidenceLevel; } MimeType FileWriterSelector::Item::GetMimeType() const { return d->m_MimeType; } std::string FileWriterSelector::Item::GetBaseDataType() const { us::Any any = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_BASEDATA_TYPE()); if (any.Empty()) return std::string(); return any.ToString(); } us::ServiceReference FileWriterSelector::Item::GetReference() const { return d->m_FileWriterRef; } long FileWriterSelector::Item::GetServiceId() const { return d->m_Id; } bool FileWriterSelector::Item::operator<(const FileWriterSelector::Item& other) const { // sort by confidence level first (ascending) if (d->m_ConfidenceLevel == other.d->m_ConfidenceLevel) { // sort by class hierarchy index (writers for more derived // based data types are considered a better match) if (d->m_BaseDataIndex == other.d->m_BaseDataIndex) { // sort by file writer service ranking return d->m_FileWriterRef < other.d->m_FileWriterRef; } return other.d->m_BaseDataIndex < d->m_BaseDataIndex; } return d->m_ConfidenceLevel < other.d->m_ConfidenceLevel; } FileWriterSelector::Item::Item() : d(new Impl()) {} void swap(FileWriterSelector& fws1, FileWriterSelector& fws2) { fws1.Swap(fws2); } } diff --git a/Core/Code/IO/mitkIOMimeTypes.cpp b/Core/Code/IO/mitkIOMimeTypes.cpp index cd2e7c6023..df0e75c388 100644 --- a/Core/Code/IO/mitkIOMimeTypes.cpp +++ b/Core/Code/IO/mitkIOMimeTypes.cpp @@ -1,225 +1,280 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkIOMimeTypes.h" #include "mitkCustomMimeType.h" +#include "itkGDCMImageIO.h" + namespace mitk { -std::vector IOMimeTypes::Get() +IOMimeTypes::DicomMimeType::DicomMimeType() + : CustomMimeType(DICOM_MIMETYPE_NAME()) +{ + this->AddExtension("gdcm"); + this->AddExtension("dcm"); + this->AddExtension("DCM"); + this->AddExtension("dc3"); + this->AddExtension("DC3"); + this->AddExtension("ima"); + this->AddExtension("img"); + + this->SetCategory(CATEGORY_IMAGES()); + this->SetComment("DICOM"); +} + +bool IOMimeTypes::DicomMimeType::AppliesTo(const std::string &path) const +{ + if (CustomMimeType::AppliesTo(path)) return true; + // Ask the GDCM ImageIO class directly + itk::GDCMImageIO::Pointer gdcmIO = itk::GDCMImageIO::New(); + return gdcmIO->CanReadFile(path.c_str()); +} + +IOMimeTypes::DicomMimeType* IOMimeTypes::DicomMimeType::Clone() const { - std::vector mimeTypes; + return new DicomMimeType(*this); +} + +std::vector IOMimeTypes::Get() +{ + std::vector mimeTypes; // order matters here (descending rank for mime types) - mimeTypes.push_back(NRRD_MIMETYPE()); - mimeTypes.push_back(NIFTI_MIMETYPE()); + mimeTypes.push_back(NRRD_MIMETYPE().Clone()); + mimeTypes.push_back(NIFTI_MIMETYPE().Clone()); + + mimeTypes.push_back(VTK_IMAGE_MIMETYPE().Clone()); + mimeTypes.push_back(VTK_PARALLEL_IMAGE_MIMETYPE().Clone()); + mimeTypes.push_back(VTK_IMAGE_LEGACY_MIMETYPE().Clone()); - mimeTypes.push_back(VTK_IMAGE_MIMETYPE()); - mimeTypes.push_back(VTK_PARALLEL_IMAGE_MIMETYPE()); - mimeTypes.push_back(VTK_IMAGE_LEGACY_MIMETYPE()); + mimeTypes.push_back(DICOM_MIMETYPE().Clone()); - mimeTypes.push_back(VTK_POLYDATA_MIMETYPE()); - mimeTypes.push_back(VTK_PARALLEL_POLYDATA_MIMETYPE()); - mimeTypes.push_back(VTK_POLYDATA_LEGACY_MIMETYPE()); + mimeTypes.push_back(VTK_POLYDATA_MIMETYPE().Clone()); + mimeTypes.push_back(VTK_PARALLEL_POLYDATA_MIMETYPE().Clone()); + mimeTypes.push_back(VTK_POLYDATA_LEGACY_MIMETYPE().Clone()); - mimeTypes.push_back(STEREOLITHOGRAPHY_MIMETYPE()); + mimeTypes.push_back(STEREOLITHOGRAPHY_MIMETYPE().Clone()); - mimeTypes.push_back(RAW_MIMETYPE()); - mimeTypes.push_back(POINTSET_MIMETYPE()); + mimeTypes.push_back(RAW_MIMETYPE().Clone()); + mimeTypes.push_back(POINTSET_MIMETYPE().Clone()); return mimeTypes; } CustomMimeType IOMimeTypes::VTK_IMAGE_MIMETYPE() { CustomMimeType mimeType(VTK_IMAGE_NAME()); mimeType.AddExtension("vti"); - mimeType.SetCategory("Images"); + mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_IMAGE_LEGACY_MIMETYPE() { CustomMimeType mimeType(VTK_IMAGE_LEGACY_NAME()); mimeType.AddExtension("vtk"); - mimeType.SetCategory("Images"); + mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Legacy Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_PARALLEL_IMAGE_MIMETYPE() { CustomMimeType mimeType(VTK_PARALLEL_IMAGE_NAME()); mimeType.AddExtension("pvti"); - mimeType.SetCategory("Images"); + mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Parallel Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_POLYDATA_MIMETYPE() { CustomMimeType mimeType(VTK_POLYDATA_NAME()); mimeType.AddExtension("vtp"); - mimeType.SetCategory("Surfaces"); + mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK PolyData"); return mimeType; } CustomMimeType IOMimeTypes::VTK_POLYDATA_LEGACY_MIMETYPE() { CustomMimeType mimeType(VTK_POLYDATA_LEGACY_NAME()); mimeType.AddExtension("vtk"); - mimeType.SetCategory("Surfaces"); + mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK Legacy PolyData"); return mimeType; } CustomMimeType IOMimeTypes::VTK_PARALLEL_POLYDATA_MIMETYPE() { CustomMimeType mimeType(VTK_PARALLEL_POLYDATA_NAME()); mimeType.AddExtension("pvtp"); - mimeType.SetCategory("Surfaces"); + mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK Parallel PolyData"); return mimeType; } CustomMimeType IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE() { CustomMimeType mimeType(STEREOLITHOGRAPHY_NAME()); mimeType.AddExtension("stl"); - mimeType.SetCategory("Surfaces"); + mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("Stereolithography"); return mimeType; } std::string IOMimeTypes::STEREOLITHOGRAPHY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".stl"; return name; } std::string IOMimeTypes::DEFAULT_BASE_NAME() { static std::string name = "application/vnd.mitk"; return name; } +std::string IOMimeTypes::CATEGORY_IMAGES() +{ + static std::string cat = "Images"; + return cat; +} + +std::string IOMimeTypes::CATEGORY_SURFACES() +{ + static std::string cat = "Surfaces"; + return cat; +} + std::string IOMimeTypes::VTK_IMAGE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.image"; return name; } std::string IOMimeTypes::VTK_IMAGE_LEGACY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.image.legacy"; return name; } std::string IOMimeTypes::VTK_PARALLEL_IMAGE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.image"; return name; } std::string IOMimeTypes::VTK_POLYDATA_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata"; return name; } std::string IOMimeTypes::VTK_POLYDATA_LEGACY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata.legacy"; return name; } std::string IOMimeTypes::VTK_PARALLEL_POLYDATA_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.polydata"; return name; } CustomMimeType IOMimeTypes::NRRD_MIMETYPE() { CustomMimeType mimeType(NRRD_MIMETYPE_NAME()); mimeType.AddExtension("nrrd"); mimeType.AddExtension("nhdr"); mimeType.SetCategory("Images"); mimeType.SetComment("NRRD"); return mimeType; } CustomMimeType IOMimeTypes::NIFTI_MIMETYPE() { CustomMimeType mimeType(NIFTI_MIMETYPE_NAME()); mimeType.AddExtension("nii"); mimeType.AddExtension("nii.gz"); mimeType.AddExtension("hdr"); mimeType.AddExtension("img"); mimeType.AddExtension("img.gz"); mimeType.AddExtension("img.gz"); mimeType.AddExtension("nia"); mimeType.SetCategory("Images"); mimeType.SetComment("Nifti"); return mimeType; } CustomMimeType IOMimeTypes::RAW_MIMETYPE() { CustomMimeType mimeType(RAW_MIMETYPE_NAME()); mimeType.AddExtension("raw"); mimeType.SetCategory("Images"); mimeType.SetComment("Raw data"); return mimeType; } +IOMimeTypes::DicomMimeType IOMimeTypes::DICOM_MIMETYPE() +{ + return DicomMimeType(); +} + std::string IOMimeTypes::NRRD_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.nrrd"; return name; } std::string IOMimeTypes::NIFTI_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.nifti"; return name; } std::string IOMimeTypes::RAW_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.raw"; return name; } +std::string IOMimeTypes::DICOM_MIMETYPE_NAME() +{ + static std::string name = DEFAULT_BASE_NAME() + ".image.dicom"; + return name; +} + CustomMimeType IOMimeTypes::POINTSET_MIMETYPE() { CustomMimeType mimeType(POINTSET_MIMETYPE_NAME()); mimeType.AddExtension("mps"); mimeType.SetCategory("Point Sets"); mimeType.SetComment("MITK Point Set"); return mimeType; } std::string IOMimeTypes::POINTSET_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".pointset"; return name; } } diff --git a/Core/Code/IO/mitkIOMimeTypes.h b/Core/Code/IO/mitkIOMimeTypes.h index 7645aadeab..8898ba4fcd 100644 --- a/Core/Code/IO/mitkIOMimeTypes.h +++ b/Core/Code/IO/mitkIOMimeTypes.h @@ -1,77 +1,90 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIOMIMETYPES_H #define MITKIOMIMETYPES_H #include "mitkCustomMimeType.h" #include namespace mitk { -class IOMimeTypes +class MITK_CORE_EXPORT IOMimeTypes { public: - static std::vector Get(); + class MITK_CORE_EXPORT DicomMimeType : public CustomMimeType + { + public: + DicomMimeType(); + virtual bool AppliesTo(const std::string &path) const; + virtual DicomMimeType* Clone() const; + }; + + static std::vector Get(); static std::string DEFAULT_BASE_NAME(); // application/vnd.mitk + static std::string CATEGORY_IMAGES(); // Images + static std::string CATEGORY_SURFACES(); // Surfaces + // ------------------------------ VTK formats ---------------------------------- static CustomMimeType VTK_IMAGE_MIMETYPE(); // (mitk::Image) vti static CustomMimeType VTK_IMAGE_LEGACY_MIMETYPE(); // (mitk::Image) vtk static CustomMimeType VTK_PARALLEL_IMAGE_MIMETYPE(); // (mitk::Image) pvti static CustomMimeType VTK_POLYDATA_MIMETYPE(); // (mitk::Surface) vtp, vtk static CustomMimeType VTK_POLYDATA_LEGACY_MIMETYPE(); // (mitk::Surface) vtk static CustomMimeType VTK_PARALLEL_POLYDATA_MIMETYPE(); // (mitk::Surface) pvtp static CustomMimeType STEREOLITHOGRAPHY_MIMETYPE(); // (mitk::Surface) stl static std::string STEREOLITHOGRAPHY_NAME(); // DEFAULT_BASE_NAME.stl static std::string VTK_IMAGE_NAME(); // DEFAULT_BASE_NAME.vtk.image static std::string VTK_IMAGE_LEGACY_NAME(); // DEFAULT_BASE_NAME.vtk.image.legacy static std::string VTK_PARALLEL_IMAGE_NAME(); // DEFAULT_BASE_NAME.vtk.parallel.image static std::string VTK_POLYDATA_NAME(); // DEFAULT_BASE_NAME.vtk.polydata static std::string VTK_POLYDATA_LEGACY_NAME(); // DEFAULT_BASE_NAME.vtk.polydata.legacy static std::string VTK_PARALLEL_POLYDATA_NAME(); // DEFAULT_BASE_NAME.vtk.parallel.polydata // ------------------------- Image formats (ITK based) -------------------------- static CustomMimeType NRRD_MIMETYPE(); // nrrd, nhdr static CustomMimeType NIFTI_MIMETYPE(); static CustomMimeType RAW_MIMETYPE(); // raw + static DicomMimeType DICOM_MIMETYPE(); static std::string NRRD_MIMETYPE_NAME(); // DEFAULT_BASE_NAME.nrrd static std::string NIFTI_MIMETYPE_NAME(); static std::string RAW_MIMETYPE_NAME(); // DEFAULT_BASE_NAME.raw + static std::string DICOM_MIMETYPE_NAME(); // ------------------------------ MITK formats ---------------------------------- static CustomMimeType POINTSET_MIMETYPE(); // mps static std::string POINTSET_MIMETYPE_NAME(); // DEFAULT_BASE_NAME.pointset private: // purposely not implemented IOMimeTypes(); IOMimeTypes(const IOMimeTypes&); }; } #endif // MITKIOMIMETYPES_H diff --git a/Core/Code/IO/mitkIOUtil.cpp b/Core/Code/IO/mitkIOUtil.cpp index cbe827b419..e6efc63e21 100644 --- a/Core/Code/IO/mitkIOUtil.cpp +++ b/Core/Code/IO/mitkIOUtil.cpp @@ -1,1070 +1,1070 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkIOUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include //ITK #include //VTK #include #include #include #include #include static std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } #ifdef US_PLATFORM_WINDOWS #include #include // make the posix flags point to the obsolte bsd types on windows #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #else #include #include #include #endif #include #include static const char validLetters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // A cross-platform version of the mkstemps function static int mkstemps_compat(char* tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary files to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return -1; } /* This is where the Xs start. */ char* XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return -1; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, NULL); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif for (unsigned int count = 0; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; int fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { errno = savedErrno; return fd; } else if (errno != EEXIST) { return -1; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } // A cross-platform version of the POSIX mkdtemp function static char* mkdtemps_compat(char* tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary dirs to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary dir. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return NULL; } /* This is where the Xs start. */ char* XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return NULL; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, NULL); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif unsigned int count = 0; for (; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; #ifdef US_PLATFORM_WINDOWS int fd = _mkdir (tmpl); //, _S_IREAD | _S_IWRITE | _S_IEXEC); #else int fd = mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); #endif if (fd >= 0) { errno = savedErrno; return tmpl; } else if (errno != EEXIST) { return NULL; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return NULL; } //#endif //************************************************************** // mitk::IOUtil method definitions namespace mitk { const std::string IOUtil::DEFAULTIMAGEEXTENSION = ".nrrd"; const std::string IOUtil::DEFAULTSURFACEEXTENSION = ".stl"; const std::string IOUtil::DEFAULTPOINTSETEXTENSION = ".mps"; struct IOUtil::Impl { struct FixedReaderOptionsFunctor : public ReaderOptionsFunctorBase { FixedReaderOptionsFunctor(const IFileReader::Options& options) : m_Options(options) {} virtual bool operator()(LoadInfo& loadInfo) { IFileReader* reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader) { reader->SetOptions(m_Options); } return false; } private: const IFileReader::Options& m_Options; }; struct FixedWriterOptionsFunctor : public WriterOptionsFunctorBase { FixedWriterOptionsFunctor(const IFileReader::Options& options) : m_Options(options) {} virtual bool operator()(SaveInfo& saveInfo) { IFileWriter* writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer) { writer->SetOptions(m_Options); } return false; } private: const IFileWriter::Options& m_Options; }; static BaseData::Pointer LoadBaseDataFromFile(const std::string& path); static void SetDefaultDataNodeProperties(mitk::DataNode* node, const std::string& filePath = std::string()); }; #ifdef US_PLATFORM_WINDOWS std::string IOUtil::GetProgramPath() { char path[512]; std::size_t index = std::string(path, GetModuleFileName(NULL, path, 512)).find_last_of('\\'); return std::string(path, index); } #elif defined(US_PLATFORM_APPLE) #include std::string IOUtil::GetProgramPath() { char path[512]; uint32_t size = sizeof(path); if (_NSGetExecutablePath(path, &size) == 0) { std::size_t index = std::string(path).find_last_of('/'); std::string strPath = std::string(path, index); //const char* execPath = strPath.c_str(); //mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch(execPath,false); return strPath; } return std::string(); } #else #include #include #include std::string IOUtil::GetProgramPath() { std::stringstream ss; ss << "/proc/" << getpid() << "/exe"; char proc[512] = {0}; ssize_t ch = readlink(ss.str().c_str(), proc, 512); if (ch == -1) return std::string(); std::size_t index = std::string(proc).find_last_of('/'); return std::string(proc, index); } #endif char IOUtil::GetDirectorySeparator() { #ifdef US_PLATFORM_WINDOWS return '\\'; #else return '/'; #endif } std::string IOUtil::GetTempPath() { static std::string result; if (result.empty()) { #ifdef US_PLATFORM_WINDOWS char tempPathTestBuffer[1]; DWORD bufferLength = ::GetTempPath(1, tempPathTestBuffer); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } std::vector tempPath(bufferLength); bufferLength = ::GetTempPath(bufferLength, &tempPath[0]); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } result.assign(tempPath.begin(), tempPath.begin() + static_cast(bufferLength)); #else result = "/tmp/"; #endif } return result; } std::string IOUtil::CreateTemporaryFile(const std::string& templateName, std::string path) { ofstream tmpOutputStream; std::string returnValue = CreateTemporaryFile(tmpOutputStream,templateName,path); tmpOutputStream.close(); return returnValue; } std::string IOUtil::CreateTemporaryFile(std::ofstream& f, const std::string& templateName, std::string path) { return CreateTemporaryFile(f, std::ios_base::out | std::ios_base::trunc, templateName, path); } std::string IOUtil::CreateTemporaryFile(std::ofstream& f, std::ios_base::openmode mode, const std::string& templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += GetDirectorySeparator() + templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; int fd = mkstemps_compat(&dst_path[0], suffixlen); if(fd != -1) { path.assign(dst_path.begin(), dst_path.end() - 1); f.open(path.c_str(), mode | std::ios_base::out | std::ios_base::trunc); close(fd); } else { mitkThrow() << "Creating temporary file " << &dst_path[0] << " failed: " << GetLastErrorStr(); } return path; } std::string IOUtil::CreateTemporaryDirectory(const std::string& templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += GetDirectorySeparator() + templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; if(mkdtemps_compat(&dst_path[0], suffixlen) == NULL) { mitkThrow() << "Creating temporary directory " << &dst_path[0] << " failed: " << GetLastErrorStr(); } path.assign(dst_path.begin(), dst_path.end() - 1); return path; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string& path, DataStorage& storage) { std::vector paths; paths.push_back(path); return Load(paths, storage); } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string& path, const IFileReader::Options& options, DataStorage& storage) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::string& path) { std::vector paths; paths.push_back(path); return Load(paths); } std::vector IOUtil::Load(const std::string& path, const IFileReader::Options& options) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, NULL, NULL, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return loadInfos.front().m_Output; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::vector& paths, DataStorage& storage) { DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); std::vector loadInfos; for (std::vector::const_iterator iter = paths.begin(), iterEnd = paths.end(); iter != iterEnd; ++iter) { LoadInfo loadInfo(*iter); loadInfos.push_back(loadInfo); } std::string errMsg = Load(loadInfos, nodeResult, &storage, NULL); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::vector& paths) { std::vector result; std::vector loadInfos; for (std::vector::const_iterator iter = paths.begin(), iterEnd = paths.end(); iter != iterEnd; ++iter) { LoadInfo loadInfo(*iter); loadInfos.push_back(loadInfo); } std::string errMsg = Load(loadInfos, NULL, NULL, NULL); if (!errMsg.empty()) { mitkThrow() << errMsg; } for (std::vector::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd; ++iter) { result.insert(result.end(), iter->m_Output.begin(), iter->m_Output.end()); } return result; } int IOUtil::LoadFiles(const std::vector &fileNames, DataStorage& ds) { return static_cast(Load(fileNames, ds)->Size()); } DataStorage::Pointer IOUtil::LoadFiles(const std::vector& fileNames) { mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); Load(fileNames, *ds); return ds.GetPointer(); } BaseData::Pointer IOUtil::LoadBaseData(const std::string& path) { return Impl::LoadBaseDataFromFile(path); } BaseData::Pointer IOUtil::Impl::LoadBaseDataFromFile(const std::string& path) { std::vector baseDataList = Load(path); // The Load(path) call above should throw an exception if nothing could be loaded assert(!baseDataList.empty()); return baseDataList.front(); } DataNode::Pointer IOUtil::LoadDataNode(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(baseData); Impl::SetDefaultDataNodeProperties(node, path); return node; } Image::Pointer IOUtil::LoadImage(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::Image::Pointer image = dynamic_cast(baseData.GetPointer()); if(image.IsNull()) { mitkThrow() << path << " is not a mitk::Image but a " << baseData->GetNameOfClass(); } return image; } Surface::Pointer IOUtil::LoadSurface(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::Surface::Pointer surface = dynamic_cast(baseData.GetPointer()); if(surface.IsNull()) { mitkThrow() << path << " is not a mitk::Surface but a " << baseData->GetNameOfClass(); } return surface; } PointSet::Pointer IOUtil::LoadPointSet(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::PointSet::Pointer pointset = dynamic_cast(baseData.GetPointer()); if(pointset.IsNull()) { mitkThrow() << path << " is not a mitk::PointSet but a " << baseData->GetNameOfClass(); } return pointset; } std::string IOUtil::Load(std::vector& loadInfos, DataStorage::SetOfObjects* nodeResult, DataStorage* ds, ReaderOptionsFunctorBase* optionsCallback) { if (loadInfos.empty()) { return "No input files given"; } int filesToRead = loadInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2*filesToRead); std::string errMsg; std::map usedReaderItems; for(std::vector::iterator infoIter = loadInfos.begin(), infoIterEnd = loadInfos.end(); infoIter != infoIterEnd; ++infoIter) { std::vector readers = infoIter->m_ReaderSelector.Get(); if (readers.empty()) { errMsg += "No reader available for '" + infoIter->m_Path + "'\n"; continue; } bool callOptionsCallback = readers.size() > 1 || !readers.front().GetReader()->GetOptions().empty(); // check if we already used a reader which should be re-used std::vector currMimeTypes = infoIter->m_ReaderSelector.GetMimeTypes(); std::string selectedMimeType; for (std::vector::const_iterator mimeTypeIter = currMimeTypes.begin(), mimeTypeIterEnd = currMimeTypes.end(); mimeTypeIter != mimeTypeIterEnd; ++mimeTypeIter) { std::map::const_iterator oldSelectedItemIter = usedReaderItems.find(mimeTypeIter->GetName()); if (oldSelectedItemIter != usedReaderItems.end()) { // we found an already used item for a mime-type which is contained // in the current reader set, check all current readers if there service // id equals the old reader for (std::vector::const_iterator currReaderItem = readers.begin(), currReaderItemEnd = readers.end(); currReaderItem != currReaderItemEnd; ++currReaderItem) { if (currReaderItem->GetMimeType().GetName() == mimeTypeIter->GetName() && currReaderItem->GetServiceId() == oldSelectedItemIter->second.GetServiceId() && currReaderItem->GetConfidenceLevel() >= oldSelectedItemIter->second.GetConfidenceLevel()) { // okay, we used the same reader already, re-use its options selectedMimeType = mimeTypeIter->GetName(); callOptionsCallback = false; infoIter->m_ReaderSelector.Select(oldSelectedItemIter->second.GetServiceId()); infoIter->m_ReaderSelector.GetSelected().GetReader()->SetOptions( oldSelectedItemIter->second.GetReader()->GetOptions()); break; } } if (!selectedMimeType.empty()) break; } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(*infoIter); if (!callOptionsCallback && !infoIter->m_Cancel) { usedReaderItems.erase(selectedMimeType); FileReaderSelector::Item selectedItem = infoIter->m_ReaderSelector.GetSelected(); usedReaderItems.insert(std::make_pair(selectedItem.GetMimeType().GetName(), selectedItem)); } } if (infoIter->m_Cancel) { errMsg += "Reading operation(s) cancelled."; break; } IFileReader* reader = infoIter->m_ReaderSelector.GetSelected().GetReader(); if (reader == NULL) { errMsg += "Unexpected NULL reader."; break; } // Do the actual reading try { DataStorage::SetOfObjects::Pointer nodes; if (ds != NULL) { nodes = reader->Read(*ds); } else { nodes = DataStorage::SetOfObjects::New(); std::vector baseData = reader->Read(); for (std::vector::iterator iter = baseData.begin(); iter != baseData.end(); ++iter) { if (iter->IsNotNull()) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); nodes->InsertElement(nodes->Size(), node); } } } for (DataStorage::SetOfObjects::ConstIterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { const mitk::DataNode::Pointer& node = nodeIter->Value(); mitk::BaseData::Pointer data = node->GetData(); if (data.IsNull()) { continue; } mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New(infoIter->m_Path); data->SetProperty("path", pathProp); infoIter->m_Output.push_back(data); if (nodeResult) { nodeResult->push_back(nodeIter->Value()); } } if (infoIter->m_Output.empty() || (nodeResult && nodeResult->Size() == 0)) { errMsg += "Unknown read error occurred reading " + infoIter->m_Path; } } catch (const std::exception& e) { errMsg += "Exception occured when reading file " + infoIter->m_Path + ":\n" + e.what() + "\n\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToRead; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2*filesToRead); return errMsg; } void IOUtil::Save(const BaseData* data, const std::string& path) { Save(data, path, IFileWriter::Options()); } void IOUtil::Save(const BaseData* data, const std::string& path, const IFileWriter::Options& options) { Save(data, std::string(), path, options); } void IOUtil::Save(const BaseData* data, const std::string& mimeType, const std::string& path, bool addExtension) { Save(data, mimeType, path, IFileWriter::Options(), addExtension); } void IOUtil::Save(const BaseData* data, const std::string& mimeType, const std::string& path, const IFileWriter::Options& options, bool addExtension) { std::string errMsg; if (options.empty()) { errMsg = Save(data, mimeType, path, NULL, addExtension); } else { Impl::FixedWriterOptionsFunctor optionsCallback(options); errMsg = Save(data, mimeType, path, &optionsCallback, addExtension); } if (!errMsg.empty()) { mitkThrow() << errMsg; } } void IOUtil::Save(std::vector& saveInfos) { std::string errMsg = Save(saveInfos, NULL); if (!errMsg.empty()) { mitkThrow() << errMsg; } } bool IOUtil::SaveImage(mitk::Image::Pointer image, const std::string& path) { Save(image, path); return true; } bool IOUtil::SaveSurface(Surface::Pointer surface, const std::string& path) { Save(surface, path); return true; } bool IOUtil::SavePointSet(PointSet::Pointer pointset, const std::string& path) { Save(pointset, path); return true; } bool IOUtil::SaveBaseData( mitk::BaseData* data, const std::string& path) { Save(data, path); return true; } std::string IOUtil::Save(const BaseData* data, const std::string& mimeTypeName, const std::string& path, WriterOptionsFunctorBase* optionsCallback, bool addExtension) { if (path.empty()) { return "No output filename given"; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); SaveInfo saveInfo(data, mimeType, path); std::string ext = itksys::SystemTools::GetFilenameExtension(path); if (saveInfo.m_WriterSelector.IsEmpty()) { - return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() + + return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() + (mimeType.IsValid() ? (std::string(" and mime-type ") + mimeType.GetName()) : std::string()) + (ext.empty() ? std::string() : (std::string(" with extension ") + ext)); } // Add an extension if not already specified if (ext.empty() && addExtension) { ext = saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front(); } std::vector infos; infos.push_back(saveInfo); return Save(infos, optionsCallback); } std::string IOUtil::Save(std::vector& saveInfos, WriterOptionsFunctorBase* optionsCallback) { if (saveInfos.empty()) { return "No data for saving available"; } int filesToWrite = saveInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2*filesToWrite); std::string errMsg; std::set usedSaveInfos; for (std::vector::iterator infoIter = saveInfos.begin(), infoIterEnd = saveInfos.end(); infoIter != infoIterEnd; ++infoIter) { const std::string baseDataType = infoIter->m_BaseData->GetNameOfClass(); std::vector writers = infoIter->m_WriterSelector.Get(); // Error out if no compatible Writer was found if (writers.empty()) { errMsg += std::string("No writer available for ") + baseDataType + " data.\n"; continue; } bool callOptionsCallback = writers.size() > 1 || !writers[0].GetWriter()->GetOptions().empty(); // check if we already used a writer for this base data type // which should be re-used std::set::const_iterator oldSaveInfoIter = usedSaveInfos.find(*infoIter); if (oldSaveInfoIter != usedSaveInfos.end()) { // we previously saved a base data object of the same data with the same mime-type, // check if the same writer is contained in the current writer set and if the // confidence level matches FileWriterSelector::Item oldSelectedItem = oldSaveInfoIter->m_WriterSelector.Get(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); for (std::vector::const_iterator currWriterItem = writers.begin(), currWriterItemEnd = writers.end(); currWriterItem != currWriterItemEnd; ++currWriterItem) { if (currWriterItem->GetServiceId() == oldSelectedItem.GetServiceId() && currWriterItem->GetConfidenceLevel() >= oldSelectedItem.GetConfidenceLevel()) { // okay, we used the same writer already, re-use its options callOptionsCallback = false; infoIter->m_WriterSelector.Select(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); infoIter->m_WriterSelector.GetSelected().GetWriter()->SetOptions( oldSelectedItem.GetWriter()->GetOptions()); break; } } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(*infoIter); if (!callOptionsCallback && !infoIter->m_Cancel) { usedSaveInfos.erase(*infoIter); usedSaveInfos.insert(*infoIter); } } if (infoIter->m_Cancel) { errMsg += "Writing operation(s) cancelled."; break; } IFileWriter* writer = infoIter->m_WriterSelector.GetSelected().GetWriter(); if (writer == NULL) { errMsg += "Unexpected NULL writer."; break; } // Do the actual writing try { writer->SetOutputLocation(infoIter->m_Path); writer->Write(); } catch(const std::exception& e) { errMsg += std::string("Exception occurred when writing to ") + infoIter->m_Path + ":\n" + e.what() + "\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToWrite; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2*filesToWrite); return errMsg; } // This method can be removed after the deprecated LoadDataNode() method was removed void IOUtil::Impl::SetDefaultDataNodeProperties(DataNode* node, const std::string& filePath) { // path mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenamePath(filePath) ); node->SetProperty(StringProperty::PATH, pathProp); // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if(nameProp.IsNull() || (strcmp(nameProp->GetValue(),"No Name!")==0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer() ); if(baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(),"No Name!")==0)) { // name neither defined in node, nor in BaseData -> name = filename nameProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenameWithoutExtension(filePath)); node->SetProperty("name", nameProp); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue()); node->SetProperty("name", nameProp); } } // visibility if(!node->GetProperty("visible")) { node->SetVisibility(true); } } IOUtil::SaveInfo::SaveInfo(const BaseData* baseData, const MimeType& mimeType, const std::string& path) : m_BaseData(baseData) , m_WriterSelector(baseData, mimeType.GetName(), path) , m_MimeType(mimeType.IsValid() ? mimeType // use the original mime-type : (m_WriterSelector.IsEmpty() ? mimeType // no writer found, use the original invalid mime-type : m_WriterSelector.GetDefault().GetMimeType() // use the found default mime-type ) ) , m_Path(path) , m_Cancel(false) { } bool IOUtil::SaveInfo::operator<(const IOUtil::SaveInfo& other) const { int r = strcmp(m_BaseData->GetNameOfClass(), other.m_BaseData->GetNameOfClass()); if (r == 0) { return m_WriterSelector.GetSelected().GetMimeType() < other.m_WriterSelector.GetSelected().GetMimeType(); } return r < 0; } IOUtil::LoadInfo::LoadInfo(const std::string& path) : m_Path(path) , m_ReaderSelector(path) , m_Cancel(false) { } } diff --git a/Core/Code/IO/mitkMimeType.cpp b/Core/Code/IO/mitkMimeType.cpp index 005d7d4092..e1bdf713e8 100644 --- a/Core/Code/IO/mitkMimeType.cpp +++ b/Core/Code/IO/mitkMimeType.cpp @@ -1,136 +1,138 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMimeType.h" #include "mitkCustomMimeType.h" +#include + namespace mitk { struct MimeType::Impl : us::SharedData { Impl() - : m_Rank(-1) + : m_CustomMimeType(new CustomMimeType()) + , m_Rank(-1) , m_Id(-1) {} Impl(const CustomMimeType& x, int rank, long id) - : m_Name(x.GetName()) - , m_Category(x.GetCategory()) - , m_Extensions(x.GetExtensions()) - , m_Comment(x.GetComment()) + : m_CustomMimeType(x.Clone()) , m_Rank(rank) , m_Id(id) {} - std::string m_Name; - std::string m_Category; - std::vector m_Extensions; - std::string m_Comment; + std::auto_ptr m_CustomMimeType; int m_Rank; long m_Id; }; MimeType::MimeType() : m_Data(new Impl) { } MimeType::MimeType(const MimeType& other) : m_Data(other.m_Data) { } MimeType::MimeType(const CustomMimeType& x, int rank, long id) : m_Data(new Impl(x, rank, id)) { } MimeType::~MimeType() { } MimeType& MimeType::operator=(const MimeType& other) { m_Data = other.m_Data; return *this; } bool MimeType::operator==(const MimeType& other) const { - return m_Data->m_Name == other.GetName(); + return this->GetName() == other.GetName(); } bool MimeType::operator<(const MimeType& other) const { if (m_Data->m_Rank != other.m_Data->m_Rank) { return m_Data->m_Rank < other.m_Data->m_Rank; } return other.m_Data->m_Id < m_Data->m_Id; } std::string MimeType::GetName() const { - return m_Data->m_Name; + return m_Data->m_CustomMimeType->GetName(); } std::string MimeType::GetCategory() const { - return m_Data->m_Category; + return m_Data->m_CustomMimeType->GetCategory(); } std::vector MimeType::GetExtensions() const { - return m_Data->m_Extensions; + return m_Data->m_CustomMimeType->GetExtensions(); } std::string MimeType::GetComment() const { - return m_Data->m_Comment; + return m_Data->m_CustomMimeType->GetComment(); +} + +bool MimeType::AppliesTo(const std::string& path) const +{ + return m_Data->m_CustomMimeType->AppliesTo(path); } bool MimeType::IsValid() const { - return m_Data.Data() != NULL && !m_Data->m_Name.empty(); + return m_Data.Data() != NULL && m_Data->m_CustomMimeType.get() != NULL && !m_Data->m_CustomMimeType->GetName().empty(); } void MimeType::Swap(MimeType& m) { m_Data.Swap(m.m_Data); } void swap(MimeType& m1, MimeType& m2) { m1.Swap(m2); } std::ostream& operator<<(std::ostream& os, const MimeType& mimeType) { os << mimeType.GetName() << " (" << mimeType.GetCategory() << ", " << mimeType.GetComment() << ") "; std::vector extensions = mimeType.GetExtensions(); for (std::vector::const_iterator iter = extensions.begin(), endIter = extensions.end(); iter != endIter; ++iter) { if (iter != extensions.begin()) os << ", "; os << *iter; } return os; } } diff --git a/Core/Code/IO/mitkMimeType.h b/Core/Code/IO/mitkMimeType.h index 17abd4d2e6..299294a339 100644 --- a/Core/Code/IO/mitkMimeType.h +++ b/Core/Code/IO/mitkMimeType.h @@ -1,78 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKMIMETYPE_H #define MITKMIMETYPE_H #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4251) #endif namespace mitk { class CustomMimeType; class MITK_CORE_EXPORT MimeType { public: MimeType(); MimeType(const MimeType& other); MimeType(const CustomMimeType& x, int rank, long id); ~MimeType(); MimeType& operator=(const MimeType& other); bool operator==(const MimeType& other) const; bool operator<(const MimeType& other) const; std::string GetName() const; std::string GetCategory() const; std::vector GetExtensions() const; std::string GetComment() const; + bool AppliesTo(const std::string& path) const; + bool IsValid() const; void Swap(MimeType& m); private: struct Impl; // Use C++11 shared_ptr instead us::SharedDataPointer m_Data; }; void swap(MimeType& m1, MimeType& m2); std::ostream& operator<<(std::ostream& os, const MimeType& mimeType); } #ifdef _MSC_VER #pragma warning(pop) #endif #endif // MITKMIMETYPE_H diff --git a/Core/Code/Interactions/mitkEventInformer.h b/Core/Code/Interactions/mitkEventInformer.h index 7aa3432720..18fc9873e0 100644 --- a/Core/Code/Interactions/mitkEventInformer.h +++ b/Core/Code/Interactions/mitkEventInformer.h @@ -1,43 +1,43 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkEventInformer_h #define mitkEventInformer_h #include "mitkInteractionEventObserver.h" #include "mitkInteractionEvent.h" -#include +#include namespace mitk { /** * Serves as an interface for the micro service that takes care of informing/registering InteractionEventObserver */ struct EventInformerService { virtual ~EventInformerService() { } virtual void RegisterObserver(InteractionEventObserver::Pointer InteractionEventObserver) = 0; virtual void UnRegisterObserver(InteractionEventObserver::Pointer InteractionEventObserver) = 0; virtual void NotifyObservers(InteractionEvent::Pointer interactionEvent) = 0; }; } /* namespace mitk */ -US_DECLARE_SERVICE_INTERFACE(mitk::EventInformerService, "EventInformerService/1.0") +MITK_DECLARE_SERVICE_INTERFACE(mitk::EventInformerService, "EventInformerService/1.0") #endif diff --git a/Core/Code/Interfaces/mitkIDataNodeReader.h b/Core/Code/Interfaces/mitkIDataNodeReader.h index 183c6210e7..dc46c0fc5c 100644 --- a/Core/Code/Interfaces/mitkIDataNodeReader.h +++ b/Core/Code/Interfaces/mitkIDataNodeReader.h @@ -1,60 +1,60 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIDATANODEREADER_H #define MITKIDATANODEREADER_H #include -#include +#include namespace mitk { class DataStorage; /** * \ingroup MicroServices_Interfaces * * This interface provides methods to load data from the local filesystem * into a given mitk::DataStorage. * * \deprecatedSince{2014_03} Use mitk::IFileReader instead */ struct IDataNodeReader { virtual ~IDataNodeReader() {} /** * Reads the local file given by fileName and constructs one or more * mitk::DataNode instances which are added to the given mitk::DataStorage storage. * * \param fileName The absolute path to a local file. * \param storage The mitk::DataStorage which will contain the constructed data nodes. * \return The number of constructed mitk::DataNode instances. * * \note Errors during reading the file or constructing the data node should be expressed by * throwing appropriate exceptions. * * \see mitk::DataNodeFactory */ virtual int Read(const std::string& fileName, mitk::DataStorage& storage) = 0; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::IDataNodeReader, "org.mitk.IDataNodeReader") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IDataNodeReader, "org.mitk.IDataNodeReader") #endif // MITKIDATANODEREADER_H diff --git a/Core/Code/Interfaces/mitkIFileReader.h b/Core/Code/Interfaces/mitkIFileReader.h index 47eadf3b6d..f3d85f371a 100644 --- a/Core/Code/Interfaces/mitkIFileReader.h +++ b/Core/Code/Interfaces/mitkIFileReader.h @@ -1,100 +1,100 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef IFileReader_H_HEADER_INCLUDED_C1E7E521 #define IFileReader_H_HEADER_INCLUDED_C1E7E521 #include #include -#include +#include namespace mitk { class BaseData; } namespace itk { template class SmartPointer; } namespace mitk { /** * \brief The common interface for all MITK file readers. * \ingroup IO * * Implementations of this interface must be registered as a service * to make themselve available via the service registry. If the * implementation is state-full, the service should be registered using * a PrototypeServiceFactory. * * The file reader implementation is associated with a mime-type, specified * in the service property PROP_MIMETYPE(). The specified mime-type should * have a corresponding CustomMimeType service object, registered by the reader * or some other party. * * It is recommended to derive new implementations from AbstractFileReader or * from AbstractFileIO (if both reader and writer is implemented), * which provide correct service registration semantics. * * \sa AbstractFileReader * \sa AbstractFileIO * \sa CustomMimeType * \sa FileReaderRegistry * \sa IFileWriter */ struct MITK_CORE_EXPORT IFileReader : public IFileIO { virtual ~IFileReader(); /** * \brief Set the input file name. * \param location The file name to read from. */ virtual void SetInput(const std::string& location) = 0; virtual void SetInput(const std::string &location, std::istream* is) = 0; virtual std::string GetInputLocation() const = 0; virtual std::istream* GetInputStream() const = 0; /** * \brief Reads the specified file or input stream and returns its contents. * * \return A list of created BaseData objects. * \throws mitk::Exception */ virtual std::vector > Read() = 0; /** * \brief Reads the specified file or input stream, loading its * contents into the provided DataStorage. * * \param ds The DataStorage to which the data is added. * \return The set of added DataNodes to \c ds. * \throws mitk::Exception */ virtual DataStorage::SetOfObjects::Pointer Read(mitk::DataStorage& ds) = 0; }; } // namespace mitk -US_DECLARE_SERVICE_INTERFACE(mitk::IFileReader, "org.mitk.IFileReader") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IFileReader, "org.mitk.IFileReader") #endif /* IFileReader_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Core/Code/Interfaces/mitkIFileWriter.h b/Core/Code/Interfaces/mitkIFileWriter.h index 4c2d463a6a..d155441686 100644 --- a/Core/Code/Interfaces/mitkIFileWriter.h +++ b/Core/Code/Interfaces/mitkIFileWriter.h @@ -1,85 +1,85 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef IFileWriter_H_HEADER_INCLUDED_C1E7E521 #define IFileWriter_H_HEADER_INCLUDED_C1E7E521 #include -#include +#include namespace mitk { class BaseData; } namespace mitk { /** * \brief The common interface of all MITK file writers. * * Implementations of this interface must be registered as a service * to make themselve available via the service registry. If the * implementation is state-full, the service should be registered using * a PrototypeServiceFactory. * * The file writer implementation is associated with a mime-type, specified * in the service property PROP_MIMETYPE() and a mitk::BaseData sub-class * as specified in the PROP_BASEDATA_TYPE() service property. * The specified mime-type should have a corresponding CustomMimeType service * object, registered by the reader or some other party. * * It is recommended to derive new implementations from AbstractFileWriter or * AbstractFileIO (if both reader and writer is implemented), * which provide correct service registration semantics. * * \sa AbstractFileWriter * \sa AbstractFileIO * \sa CustomMimeType * \sa FileWriterRegistry * \sa IFileReader */ struct MITK_CORE_EXPORT IFileWriter : public IFileIO { virtual ~IFileWriter(); virtual void SetInput(const BaseData* data) = 0; virtual const BaseData* GetInput() const = 0; virtual void SetOutputLocation(const std::string& location) = 0; virtual std::string GetOutputLocation() const = 0; virtual void SetOutputStream(const std::string& location, std::ostream* os) = 0; virtual std::ostream* GetOutputStream() const = 0; virtual void Write() = 0; /** * @brief Service property name for the supported mitk::BaseData sub-class * * The property value must be of type \c std::string. * * @return The property name. */ static std::string PROP_BASEDATA_TYPE(); }; } // namespace mitk -US_DECLARE_SERVICE_INTERFACE(mitk::IFileWriter, "org.mitk.IFileWriter") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IFileWriter, "org.mitk.IFileWriter") #endif /* IFileWriter_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Core/Code/Interfaces/mitkIMimeTypeProvider.h b/Core/Code/Interfaces/mitkIMimeTypeProvider.h index f8431a2ccd..c8afe752ce 100644 --- a/Core/Code/Interfaces/mitkIMimeTypeProvider.h +++ b/Core/Code/Interfaces/mitkIMimeTypeProvider.h @@ -1,73 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIMIMETYPEPROVIDER_H #define MITKIMIMETYPEPROVIDER_H #include #include -#include +#include #include #include namespace mitk { /** * @brief The IMimeTypeProvider service interface allows to query all registered * mime types. * * Mime types are added to the system by registering a service object of type * CustomMimeType and the registered mime types can be queried bei either using direct * look-ups in the service registry or calling the methods of this service interface. * * This service interface also allows to infer the mime type of a file on the file * system. The heuristics for infering the actual mime type is implementation specific. * * @note This is a core service * * @sa CustomMimeType * @sa CoreServices::GetMimeTypeProvider() */ struct MITK_CORE_EXPORT IMimeTypeProvider { virtual ~IMimeTypeProvider(); virtual std::vector GetMimeTypes() const = 0; virtual std::vector GetMimeTypesForFile(const std::string& filePath) const = 0; - virtual std::vector GetMimeTypesForExtension(const std::string& extension) const = 0; - virtual std::vector GetMimeTypesForCategory(const std::string& category) const = 0; virtual MimeType GetMimeTypeForName(const std::string& name) const = 0; /** * @brief Get a sorted and unique list of mime-type categories. * @return A sorted, unique list of mime-type categories. */ virtual std::vector GetCategories() const = 0; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::IMimeTypeProvider, "org.mitk.IMimeTypeProvider") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IMimeTypeProvider, "org.mitk.IMimeTypeProvider") #endif // MITKIMIMETYPEPROVIDER_H diff --git a/Core/Code/Interfaces/mitkIPersistenceService.h b/Core/Code/Interfaces/mitkIPersistenceService.h index 14a9fd361e..ea7da5fbd4 100644 --- a/Core/Code/Interfaces/mitkIPersistenceService.h +++ b/Core/Code/Interfaces/mitkIPersistenceService.h @@ -1,339 +1,339 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPersistenceService_h #define mitkIPersistenceService_h // mitk #include "mitkPropertyListReplacedObserver.h" #include "usServiceReference.h" #include "usModuleContext.h" #include "usGetModuleContext.h" //for microservices -#include +#include #include "mitkDataStorage.h" namespace mitk { /** * The central service for the persistence module * Basic idea is to create PropertyLists with a unique id using AddPropertyList(). A consumer * of this interface can write arbitrary information into this propertylist * Calling Save() and Load() will cause the Service to save and load the current set of propertlists from * a file in the user directory. * Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application * start/stop. * Moreover, depending on the backend type, the service is connected to the SceneSerialization module, i.e. * the user will be asked whether to save/load the propertlists in/from the current ".mitk" file that is selected * by the user. */ class MITK_CORE_EXPORT IPersistenceService { public: /** * If PropertyList with the given id exists, returns it. Otherwise creates a new one and returns it. * If id is empty a UUID will be created and set on the variable * If existed was passed, it is true if the PropertyList with that id existed, false otherwise * \return a valid PropertyList with a StringProperty "Id" containing the passed id */ virtual mitk::PropertyList::Pointer GetPropertyList( std::string& id, bool* existed=0 ) = 0; /** * removes the PropertyList with the given id * \return true if PropertyList existed and could be removed, false otherwise */ virtual bool RemovePropertyList( std::string& id ) = 0; /** * Get the default name of the PersistenceFile (the one that is loaded at startup) */ virtual std::string GetDefaultPersistenceFile() = 0; /** * \return The name of the Bool Property that specifies whether a DataNode is a Node carrying Persistence PropertyLists */ virtual std::string GetPersistenceNodePropertyName() = 0; /** * Creates a vector of DataNodes that contain all PropertyLists. Additionally, the DataNodes * will have the property name set to the PropertyList's id and a BoolProperty equal to GetPersistenceNodePropertyName() set to true. If ds is set the returned DataNodes will also be added to that DS. * \return vector of DataNodes with the described attributes */ virtual DataStorage::SetOfObjects::Pointer GetDataNodes(DataStorage* ds=0) = 0; /** * Searches storage for persistent DataNodes, extracts and inserts the appended property lists to this service * \return true if at least one node was found from which a PropertyList could be restored */ virtual bool RestorePropertyListsFromPersistentDataNodes(const DataStorage* storage) = 0; /** * Save the current PropertyLists to fileName. If fileName is empty, a special file in the users home directory will be used. * if appendchanges is true, the file will not replaced but first loaded, then overwritten and then replaced * \return false if an error occured (cannot write to file), true otherwise */ virtual bool Save(const std::string& fileName="", bool appendChanges=false) = 0; /** * Load PropertyLists from fileName. If fileName is empty, a special file in the users home directory will be used. * If enforeReload is false, the service will take care of modified time flags, i.e. it will not load a file * that was loaded before and did not change in the meantime or that was modified by the service itself * *ATTENTION*: If there are PropertyLists with the same id contained in the file, existing PropertyLists will be overwritten! * \see AddPropertyListReplacedObserver() * \return false if an error occured (cannot load from file), true otherwise */ virtual bool Load(const std::string& fileName="", bool enforeReload=true) = 0; /** * Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application * start/stop. */ virtual void SetAutoLoadAndSave(bool autoLoadAndSave) = 0; /** * \return whether AutoLoading is activated or not */ virtual bool GetAutoLoadAndSave() = 0; /** * adds a observer which is informed if a propertyList gets replaced during a Load() procedure */ virtual void AddPropertyListReplacedObserver( PropertyListReplacedObserver* observer ) = 0; /** * removes a specific observer */ virtual void RemovePropertyListReplacedObserver( PropertyListReplacedObserver* observer ) = 0; /** * nothing to do here */ virtual ~IPersistenceService(); }; } // MACROS FOR AUTOMATIC SAVE FUNCTION #define PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION\ us::GetModuleContext() #define PERSISTENCE_GET_SERVICE_MACRO\ mitk::IPersistenceService* persistenceService = 0;\ us::ModuleContext* context = PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION;\ if( context )\ {\ us::ServiceReference persistenceServiceRef = context->GetServiceReference();\ if( persistenceServiceRef )\ {\ persistenceService\ = dynamic_cast ( context->GetService(persistenceServiceRef) );\ }\ } #define PERSISTENCE_GET_SERVICE_METHOD_MACRO\ mitk::IPersistenceService* GetPeristenceService() const\ {\ PERSISTENCE_GET_SERVICE_MACRO\ return persistenceService;\ } #define PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ public:\ bool Save(const std::string& fileName="", bool appendChanges=false)\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ bool noError = persistenceService != 0;\ if( noError )\ this->ToPropertyList();\ if(noError)\ noError = persistenceService->Save(fileName, appendChanges);\ return noError;\ }\ bool Load(const std::string& fileName="", bool enforeReload=true)\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ bool noError = persistenceService != 0 && persistenceService->Load(fileName, enforeReload);\ if( noError )\ {\ this->FromPropertyList();\ }\ return noError;\ }\ void ToPropertyList()\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ this->InitializePropertyListReplacedObserver(persistenceService);\ if( !persistenceService )\ return;\ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME); #define PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ }\ void FromPropertyList()\ {\ mitk::IPersistenceService* persistenceService = this->GetPeristenceService();\ this->InitializePropertyListReplacedObserver(persistenceService);\ if( !persistenceService )\ return;\ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME); #define PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME)\ }\ std::string GetId() const\ {\ return ID_MEMBER_NAME;\ }\ private:\ PERSISTENCE_GET_SERVICE_METHOD_MACRO\ struct MyPropertyListReplacedObserver: public mitk::PropertyListReplacedObserver\ {\ MyPropertyListReplacedObserver()\ : m_##THE_CLASS_NAME(0), m_PersistenceService(0)\ {\ }\ ~MyPropertyListReplacedObserver()\ {\ if( m_PersistenceService )\ m_PersistenceService->RemovePropertyListReplacedObserver(this);\ }\ void AfterPropertyListReplaced( const std::string& id, mitk::PropertyList* propertyList )\ {\ if( m_##THE_CLASS_NAME && m_##THE_CLASS_NAME->GetId() == id )\ m_##THE_CLASS_NAME->FromPropertyList();\ }\ void Initialize( THE_CLASS_NAME * _##THE_CLASS_NAME, mitk::IPersistenceService* persistenceService )\ {\ m_##THE_CLASS_NAME = _##THE_CLASS_NAME;\ m_PersistenceService = persistenceService;\ if( m_PersistenceService )\ m_PersistenceService->AddPropertyListReplacedObserver(this);\ }\ private:\ THE_CLASS_NAME * m_##THE_CLASS_NAME;\ mitk::IPersistenceService* m_PersistenceService;\ };\ MyPropertyListReplacedObserver m_MyPropertyListReplacedObserver;\ void InitializePropertyListReplacedObserver(mitk::IPersistenceService* persistenceService)\ {\ static bool observerInitialized = false;\ if( observerInitialized == false && persistenceService )\ {\ m_MyPropertyListReplacedObserver.Initialize( this, persistenceService );\ observerInitialized = true;\ }\ } #define PERSISTENCE_CREATE(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE2(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE3(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE4(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Set( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Get( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE5(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME, PARAM5_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Set( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Set( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Get( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Get( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE6(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME, PARAM5_MEMBER_NAME, PARAM6_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Set( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Set( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ propList->Set( #PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Get( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Get( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ propList->Get( #PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE7(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME, PARAM5_MEMBER_NAME, PARAM6_MEMBER_NAME, PARAM7_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Set( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Set( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ propList->Set( #PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME );\ propList->Set( #PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Get( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Get( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ propList->Get( #PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME );\ propList->Get( #PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE8(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME, PARAM5_MEMBER_NAME, PARAM6_MEMBER_NAME, PARAM7_MEMBER_NAME, PARAM8_MEMBER_NAME)\ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME)\ propList->Set( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Set( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Set( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Set( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Set( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ propList->Set( #PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME );\ propList->Set( #PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME );\ propList->Set( #PARAM8_MEMBER_NAME, PARAM8_MEMBER_NAME );\ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME)\ propList->Get( #PARAM_MEMBER_NAME, PARAM_MEMBER_NAME );\ propList->Get( #PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME );\ propList->Get( #PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME );\ propList->Get( #PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME );\ propList->Get( #PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME );\ propList->Get( #PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME );\ propList->Get( #PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME );\ propList->Get( #PARAM8_MEMBER_NAME, PARAM8_MEMBER_NAME );\ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) -US_DECLARE_SERVICE_INTERFACE(mitk::IPersistenceService, "org.mitk.services.IPersistenceService") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPersistenceService, "org.mitk.services.IPersistenceService") #endif diff --git a/Core/Code/Interfaces/mitkIPropertyAliases.h b/Core/Code/Interfaces/mitkIPropertyAliases.h index 09a4f8e17a..0fe9b4118b 100644 --- a/Core/Code/Interfaces/mitkIPropertyAliases.h +++ b/Core/Code/Interfaces/mitkIPropertyAliases.h @@ -1,96 +1,96 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPropertyAliases_h #define mitkIPropertyAliases_h -#include +#include #include #include #include namespace mitk { /** \brief Interface of property aliases service. * * This service allows you to manage aliases of property names. * A property name can be mapped to more than one alias and aliases can be restricted to specific data node types. * The property view prefers to display aliases instead of genuine property names. */ class MITK_CORE_EXPORT IPropertyAliases { public: virtual ~IPropertyAliases(); /** \brief Add an alias for a specific property. * * \param[in] propertyName Name of the property. * \param[in] alias Alias for the property. * \param[in] className Optional data node class name to which this alias is restricted. * \return False if alias is an empty string. */ virtual bool AddAlias(const std::string& propertyName, const std::string& alias, const std::string& className = "") = 0; /** \brief Get aliases for a specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the returned aliases are restricted. * \return Aliases that match the input criteria. */ virtual std::vector GetAliases(const std::string& propertyName, const std::string& className = "") = 0; /** \brief Get property name that is associated to specific alias. * * \param[in] alias Alias of the property. * \param[in] className Optional data node class name to which the alias is restricted. * \return Associated property name or empty string if no property name was found. */ virtual std::string GetPropertyName(const std::string& alias, const std::string& className = "") = 0; /** \brief Check if a specific property has aliases. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the aliases are restricted. * \return True if the property has aliases, false otherwise. */ virtual bool HasAliases(const std::string& propertyName, const std::string& className = "") = 0; /** \brief Remove specific alias of a certain property. * * \param[in] propertyName Name of the property. * \param[in] alias Alias of the property. * \param[in] className Optional data node class name to which the alias is restricted. */ virtual void RemoveAlias(const std::string& propertyName, const std::string& alias, const std::string& className = "") = 0; /** \brief Remove all aliases of a specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the aliases are restricted. */ virtual void RemoveAliases(const std::string& propertyName, const std::string& className = "") = 0; /** \brief Remove all aliases of all properties. * * \param[in] className Optional data node class name to which the removal is restricted. */ virtual void RemoveAllAliases(const std::string& className = "") = 0; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::IPropertyAliases, "org.mitk.IPropertyAliases") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyAliases, "org.mitk.IPropertyAliases") #endif diff --git a/Core/Code/Interfaces/mitkIPropertyDescriptions.h b/Core/Code/Interfaces/mitkIPropertyDescriptions.h index de8e55a876..9f18057678 100644 --- a/Core/Code/Interfaces/mitkIPropertyDescriptions.h +++ b/Core/Code/Interfaces/mitkIPropertyDescriptions.h @@ -1,79 +1,79 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPropertyDescriptions_h #define mitkIPropertyDescriptions_h -#include +#include #include #include namespace mitk { /** \brief Interface of property descriptions service. * * This service allows you to manage descriptions for properties. * The property view displays descriptions of selected properties (in rich text format) at its bottom. */ class MITK_CORE_EXPORT IPropertyDescriptions { public: virtual ~IPropertyDescriptions(); /** \brief Add a description for a specific property. * * \param[in] propertyName Name of the property. * \param[in] description Description of the property. * \param[in] className Optional data node class name to which this description is restricted. * \param[in] overwrite Overwrite already existing description. * \return True if description was added successfully. */ virtual bool AddDescription(const std::string& propertyName, const std::string& description, const std::string& className = "", bool overwrite = false) = 0; /** \brief Get the description for a specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the returned description is restricted. * \return Property description or empty string if no description is available. */ virtual std::string GetDescription(const std::string& propertyName, const std::string& className = "") = 0; /** \brief Check if a specific property has a description. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which this description is restricted. * \return True if the property has a description, false otherwise. */ virtual bool HasDescription(const std::string& propertyName, const std::string& className = "") = 0; /** \brief Remove all descriptions. * * \param[in] className Optional data node class name to which this description is restricted. */ virtual void RemoveAllDescriptions(const std::string& className = "") = 0; /** \brief Remove description of specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which this description is restricted. */ virtual void RemoveDescription(const std::string& propertyName, const std::string& className = "") = 0; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::IPropertyDescriptions, "org.mitk.IPropertyDescriptions") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyDescriptions, "org.mitk.IPropertyDescriptions") #endif diff --git a/Core/Code/Interfaces/mitkIPropertyExtensions.h b/Core/Code/Interfaces/mitkIPropertyExtensions.h index 3a8c23890a..d180af5251 100644 --- a/Core/Code/Interfaces/mitkIPropertyExtensions.h +++ b/Core/Code/Interfaces/mitkIPropertyExtensions.h @@ -1,82 +1,82 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPropertyExtensions_h #define mitkIPropertyExtensions_h #include -#include +#include #include #include namespace mitk { /** \brief Interface of property extensions service. * * This service allows you to manage extensions for properties. * An extension is a class that derives from mitk::PropertyExtension. * Use extensions to attach useful metadata to your properties, e.g. the allowed range of values. * Note that you have to extend the property view if you want it to respect your custom metadata. */ class MITK_CORE_EXPORT IPropertyExtensions { public: virtual ~IPropertyExtensions(); /** \brief Add an extension to a specific property. * * \param[in] propertyName Name of the property. * \param[in] extension Property extension. * \param[in] className Optional data node class name to which the property extension is restricted. * \param[in] overwrite Overwrite already existing property extension. * \return True if extension was added successfully. */ virtual bool AddExtension(const std::string& propertyName, PropertyExtension::Pointer extension, const std::string& className = "", bool overwrite = false) = 0; /** \brief Get the extension of a specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the returned property extension is restricted. * \return Property extension or null pointer if no extension was found. */ virtual PropertyExtension::Pointer GetExtension(const std::string& propertyName, const std::string& className = "") = 0; /** \brief Check if a specific property has an extension. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the property extension is restricted. * \return True if the property has an extension, false otherwise. */ virtual bool HasExtension(const std::string& propertyName, const std::string& className = "") = 0; /** \brief Remove all property extensions. * * \param[in] className Optional data node class name to which the property extension is restricted. */ virtual void RemoveAllExtensions(const std::string& className = "") = 0; /** \brief Remove extension of a specific property. * * \param[in] propertyName Name of the property. * \param[in] className Optional data node class name to which the property extension is restricted. */ virtual void RemoveExtension(const std::string& propertyName, const std::string& className = "") = 0; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::IPropertyExtensions, "org.mitk.IPropertyExtensions") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyExtensions, "org.mitk.IPropertyExtensions") #endif diff --git a/Core/Code/Interfaces/mitkIPropertyFilters.h b/Core/Code/Interfaces/mitkIPropertyFilters.h index 8e4c3c6533..d5bbe5ca0d 100644 --- a/Core/Code/Interfaces/mitkIPropertyFilters.h +++ b/Core/Code/Interfaces/mitkIPropertyFilters.h @@ -1,86 +1,86 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkIPropertyFilters_h #define mitkIPropertyFilters_h #include -#include +#include #include #include #include namespace mitk { class PropertyFilter; /** \brief Interface of property filters service. * * This service allows you manage property name filters. * A mitk::PropertyFilter consist of blacklist and whitelist entries and can be restricted to certain data node types. * The property view only displays filtered properties if filters were found. */ class MITK_CORE_EXPORT IPropertyFilters { public: virtual ~IPropertyFilters(); /** \brief Add a property filter. * * \param[in] filter The property filter. * \param[in] className Optional data node class name to which the filter is restricted. * \param[in] overwrite Overwrite already existing filter. * \return True if filter was added auccessfully. */ virtual bool AddFilter(const PropertyFilter& filter, const std::string& className = "", bool overwrite = false) = 0; /** \brief Apply property filter to property list. * * \param[in] propertyMap Property list to which the filter is applied. * \param[in] className Optional data node class name for which the filter is chosen. * \return Filtered property list. */ virtual std::map ApplyFilter(const std::map& propertyMap, const std::string& className = "") const = 0; /** \brief Get the current property filter. * * \param[in] className Optional data node class name for which the filter is returned. * \return The current property filter. */ virtual PropertyFilter GetFilter(const std::string& className = "") const = 0; /** \brief Check if a specific data node class name has a property filter. * * \param[in] className The data node class name or empty string for global property filter. * \return True if data node class name has a filter, false otherwise. */ virtual bool HasFilter(const std::string& className = "") const = 0; /** \brief Remove all property filters. */ virtual void RemoveAllFilters() = 0; /** \brief Remove property filter for a specific data node class name. * * \param[in] className The data node class name or empty string for global property filter. */ virtual void RemoveFilter(const std::string& className = "") = 0; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::IPropertyFilters, "org.mitk.IPropertyFilters") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyFilters, "org.mitk.IPropertyFilters") #endif diff --git a/Core/Code/Interfaces/mitkIShaderRepository.h b/Core/Code/Interfaces/mitkIShaderRepository.h index 7f6cd27942..14bbd83fae 100644 --- a/Core/Code/Interfaces/mitkIShaderRepository.h +++ b/Core/Code/Interfaces/mitkIShaderRepository.h @@ -1,144 +1,144 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKISHADERREPOSITORY_H #define MITKISHADERREPOSITORY_H #include #include "mitkCommon.h" -#include "usServiceInterface.h" +#include "mitkServiceInterface.h" #include class vtkActor; class vtkShaderProgram2; namespace mitk { class DataNode; class BaseRenderer; /** * \brief Management class for vtkShader XML descriptions. * * Loads XML shader files from std::istream objects and adds default properties * for each shader object (shader uniforms) to the specified mitk::DataNode. * * Additionally, it provides a utility function for applying properties for shaders * in mappers. */ struct MITK_CORE_EXPORT IShaderRepository { struct ShaderPrivate; class MITK_CORE_EXPORT Shader : public itk::LightObject { public: mitkClassMacro( Shader, itk::LightObject ) itkFactorylessNewMacro( Self ) ~Shader(); int GetId() const; std::string GetName() const; std::string GetMaterialXml() const; protected: Shader(); void SetId(int id); void SetName(const std::string& name); void SetMaterialXml(const std::string& xml); private: // not implemented Shader(const Shader&); Shader& operator=(const Shader&); ShaderPrivate* d; }; class MITK_CORE_EXPORT ShaderProgram : public itk::LightObject { public: virtual void Activate() = 0; virtual void Deactivate() = 0; mitkClassMacro( ShaderProgram, itk::LightObject ) }; virtual ~IShaderRepository(); virtual std::list GetShaders() const = 0; /** * \brief Return the named shader. * * \param name The shader name. * \return A Shader object. * * Names might not be unique. Use the shader id to uniquely identify a shader. */ virtual Shader::Pointer GetShader(const std::string& name) const = 0; virtual ShaderProgram::Pointer CreateShaderProgram() = 0; /** * \brief Return the shader identified by the given id. * @param id The shader id. * @return The shader object or null if the id is unknown. */ virtual Shader::Pointer GetShader(int id) const = 0; /** \brief Adds all parsed shader uniforms to property list of the given DataNode; * used by mappers. */ virtual void AddDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) const = 0; /** \brief Applies shader and shader specific variables of the specified DataNode * to the VTK object by updating the shader variables of its vtkProperty. */ virtual void UpdateShaderProgram(mitk::IShaderRepository::ShaderProgram* shaderProgram, mitk::DataNode* node, mitk::BaseRenderer* renderer) const = 0; /** \brief Loads a shader from a given file. Make sure that this stream is in the XML shader format. * * \return A unique id for the loaded shader which can be used to unload it. */ virtual int LoadShader(std::istream& stream, const std::string& name) = 0; /** * \brief Unload a previously loaded shader. * \param id The unique shader id returned by LoadShader. * \return \c true if the shader id was found and the shader was successfully unloaded, * \c false otherwise. */ virtual bool UnloadShader(int id) = 0; }; } -US_DECLARE_SERVICE_INTERFACE(mitk::IShaderRepository, "org.mitk.services.IShaderRepository/1.0") +MITK_DECLARE_SERVICE_INTERFACE(mitk::IShaderRepository, "org.mitk.services.IShaderRepository/1.0") #endif // MITKISHADERREPOSITORY_H diff --git a/Core/Code/Interfaces/mitkInteractionEventObserver.h b/Core/Code/Interfaces/mitkInteractionEventObserver.h index 08f58b906e..3c9306534d 100644 --- a/Core/Code/Interfaces/mitkInteractionEventObserver.h +++ b/Core/Code/Interfaces/mitkInteractionEventObserver.h @@ -1,69 +1,69 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef InteractionEventObserver_h #define InteractionEventObserver_h #include -#include "usServiceInterface.h" +#include "mitkServiceInterface.h" #include "mitkInteractionEvent.h" namespace mitk { /** * \class InteractionEventObserver * \brief Base class to implement InteractionEventObservers. * * This class also provides state machine infrastructure, * but usage thereof is optional. See the Notify method for more information. */ struct MITK_CORE_EXPORT InteractionEventObserver { InteractionEventObserver(); virtual ~InteractionEventObserver(); /** * By this method all registered EventObersers are notified about every InteractionEvent, * the isHandled flag indicates if a DataInteractor has already handled that event. * InteractionEventObserver that trigger an action when observing an event may consider * this in order to not confuse the user by, triggering several independent action with one * single user event (such as a mouse click) * * If you want to use the InteractionEventObserver as a state machine give the event to the state machine by implementing, e.g. \code void mitk::InteractionEventObserver::Notify(InteractionEvent::Pointer interactionEvent, bool isHandled) { if (!isHandled) { this->HandleEvent(interactionEvent, NULL); } } \endcode * This overwrites the FilterEvents function of the EventStateMachine to ignore the DataNode, since InteractionEventObservers are not associated with one. virtual bool FilterEvents(InteractionEvent* interactionEvent, DataNode* dataNode); */ virtual void Notify(InteractionEvent* interactionEvent,bool isHandled) = 0; void Disable(); void Enable(); bool IsEnabled() const; private: bool m_IsEnabled; }; } /* namespace mitk */ -US_DECLARE_SERVICE_INTERFACE(mitk::InteractionEventObserver, "org.mitk.InteractionEventObserver") +MITK_DECLARE_SERVICE_INTERFACE(mitk::InteractionEventObserver, "org.mitk.InteractionEventObserver") #endif /* InteractionEventObserver_h */ diff --git a/Core/Code/Internal/mitkCoreActivator.cpp b/Core/Code/Internal/mitkCoreActivator.cpp index c919b2e87b..a030c61ea4 100644 --- a/Core/Code/Internal/mitkCoreActivator.cpp +++ b/Core/Code/Internal/mitkCoreActivator.cpp @@ -1,443 +1,461 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCoreActivator.h" // File IO #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkLegacyFileWriterService.h" +#include "mitkDicomSeriesReaderService.h" #include +#include // Micro Services #include #include #include #include #include #include #include #include #include #include void HandleMicroServicesMessages(us::MsgType type, const char* msg) { switch (type) { case us::DebugMsg: MITK_DEBUG << msg; break; case us::InfoMsg: MITK_INFO << msg; break; case us::WarningMsg: MITK_WARN << msg; break; case us::ErrorMsg: MITK_ERROR << msg; break; } } void AddMitkAutoLoadPaths(const std::string& programPath) { us::ModuleSettings::AddAutoLoadPath(programPath); #ifdef __APPLE__ // Walk up three directories since that is where the .dylib files are located // for build trees. std::string additionalPath = programPath; bool addPath = true; for(int i = 0; i < 3; ++i) { std::size_t index = additionalPath.find_last_of('/'); if (index != std::string::npos) { additionalPath = additionalPath.substr(0, index); } else { addPath = false; break; } } if (addPath) { us::ModuleSettings::AddAutoLoadPath(additionalPath); } #endif } class ShaderRepositoryTracker : public us::ServiceTracker { public: ShaderRepositoryTracker() : Superclass(us::GetModuleContext()) { } virtual void Close() { us::GetModuleContext()->RemoveModuleListener(this, &ShaderRepositoryTracker::HandleModuleEvent); Superclass::Close(); } virtual void Open() { us::GetModuleContext()->AddModuleListener(this, &ShaderRepositoryTracker::HandleModuleEvent); Superclass::Open(); } private: typedef us::ServiceTracker Superclass; TrackedType AddingService(const ServiceReferenceType &reference) { mitk::IShaderRepository* shaderRepo = Superclass::AddingService(reference); if (shaderRepo) { // Add all existing shaders from modules to the new shader repository. // If the shader repository is registered in a modules activator, the // GetLoadedModules() function call below will also return the module // which is currently registering the repository. The HandleModuleEvent // method contains code to avoid double registrations due to a fired // ModuleEvent::LOADED event after the activators Load() method finished. std::vector modules = us::ModuleRegistry::GetLoadedModules(); for (std::vector::const_iterator iter = modules.begin(), endIter = modules.end(); iter != endIter; ++iter) { this->AddModuleShaderToRepository(*iter, shaderRepo); } m_ShaderRepositories.push_back(shaderRepo); } return shaderRepo; } void RemovedService(const ServiceReferenceType& /*reference*/, TrackedType tracked) { m_ShaderRepositories.erase(std::remove(m_ShaderRepositories.begin(), m_ShaderRepositories.end(), tracked), m_ShaderRepositories.end()); } void HandleModuleEvent(const us::ModuleEvent moduleEvent) { if (moduleEvent.GetType() == us::ModuleEvent::LOADED) { std::vector shaderRepos; for (std::map > >::const_iterator shaderMapIter = m_ModuleIdToShaderIds.begin(), shaderMapEndIter = m_ModuleIdToShaderIds.end(); shaderMapIter != shaderMapEndIter; ++shaderMapIter) { if (shaderMapIter->second.find(moduleEvent.GetModule()->GetModuleId()) == shaderMapIter->second.end()) { shaderRepos.push_back(shaderMapIter->first); } } AddModuleShadersToRepositories(moduleEvent.GetModule(), shaderRepos); } else if (moduleEvent.GetType() == us::ModuleEvent::UNLOADED) { RemoveModuleShadersFromRepositories(moduleEvent.GetModule(), m_ShaderRepositories); } } void AddModuleShadersToRepositories(us::Module* module, const std::vector& shaderRepos) { // search and load shader files std::vector shaderResources = module->FindResources("Shaders", "*.xml", true); for (std::vector::iterator i = shaderResources.begin(); i != shaderResources.end(); ++i) { if (*i) { us::ModuleResourceStream rs(*i); for (std::vector::const_iterator shaderRepoIter = shaderRepos.begin(), shaderRepoEndIter = shaderRepos.end(); shaderRepoIter != shaderRepoEndIter; ++shaderRepoIter) { int id = (*shaderRepoIter)->LoadShader(rs, i->GetBaseName()); if (id >= 0) { m_ModuleIdToShaderIds[*shaderRepoIter][module->GetModuleId()].push_back(id); } } rs.seekg(0, std::ios_base::beg); } } } void AddModuleShaderToRepository(us::Module* module, mitk::IShaderRepository* shaderRepo) { std::vector shaderRepos; shaderRepos.push_back(shaderRepo); this->AddModuleShadersToRepositories(module, shaderRepos); } void RemoveModuleShadersFromRepositories(us::Module* module, const std::vector& shaderRepos) { for (std::vector::const_iterator shaderRepoIter = shaderRepos.begin(), shaderRepoEndIter = shaderRepos.end(); shaderRepoIter != shaderRepoEndIter; ++shaderRepoIter) { std::map >& moduleIdToShaderIds = m_ModuleIdToShaderIds[*shaderRepoIter]; std::map >::iterator shaderIdsIter = moduleIdToShaderIds.find(module->GetModuleId()); if (shaderIdsIter != moduleIdToShaderIds.end()) { for (std::vector::iterator idIter = shaderIdsIter->second.begin(); idIter != shaderIdsIter->second.end(); ++idIter) { (*shaderRepoIter)->UnloadShader(*idIter); } moduleIdToShaderIds.erase(shaderIdsIter); } } } private: // Maps to each shader repository a map containing module ids and related // shader registration ids std::map > > m_ModuleIdToShaderIds; std::vector m_ShaderRepositories; }; class FixedNiftiImageIO : public itk::NiftiImageIO { public: /** Standard class typedefs. */ typedef FixedNiftiImageIO Self; typedef itk::NiftiImageIO Superclass; typedef itk::SmartPointer< Self > Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(FixedNiftiImageIO, Superclass) virtual bool SupportsDimension(unsigned long dim) { return dim > 1 && dim < 5; } }; void MitkCoreActivator::Load(us::ModuleContext* context) { // Handle messages from CppMicroServices us::installMsgHandler(HandleMicroServicesMessages); this->m_Context = context; // Add the current application directory to the auto-load paths. // This is useful for third-party executables. std::string programPath = mitk::IOUtil::GetProgramPath(); if (programPath.empty()) { MITK_WARN << "Could not get the program path."; } else { AddMitkAutoLoadPaths(programPath); } m_ShaderRepositoryTracker.reset(new ShaderRepositoryTracker); //m_RenderingManager = mitk::RenderingManager::New(); //context->RegisterService(renderingManager.GetPointer()); m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); context->RegisterService(m_PlanePositionManager.get()); m_PropertyAliases.reset(new mitk::PropertyAliases); context->RegisterService(m_PropertyAliases.get()); m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService(m_PropertyDescriptions.get()); m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService(m_PropertyExtensions.get()); m_PropertyFilters.reset(new mitk::PropertyFilters); context->RegisterService(m_PropertyFilters.get()); m_MimeTypeProvider.reset(new mitk::MimeTypeProvider); m_MimeTypeProvider->Start(); m_MimeTypeProviderReg = context->RegisterService(m_MimeTypeProvider.get()); this->RegisterDefaultMimeTypes(); this->RegisterItkReaderWriter(); this->RegisterVtkReaderWriter(); // Add custom Reader / Writer Services m_FileReaders.push_back(new mitk::PointSetReaderService()); m_FileWriters.push_back(new mitk::PointSetWriterService()); + m_FileReaders.push_back(new mitk::DicomSeriesReaderService()); m_FileReaders.push_back(new mitk::RawImageFileReaderService()); m_ShaderRepositoryTracker->Open(); /* There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. This code is left here as a reminder, just in case we might need to do that some time. vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); vtkObjectFactory::RegisterFactory( textureFactory ); textureFactory->Delete(); */ this->RegisterLegacyWriter(); } void MitkCoreActivator::Unload(us::ModuleContext* ) { for(std::vector::iterator iter = m_FileReaders.begin(), endIter = m_FileReaders.end(); iter != endIter; ++iter) { delete *iter; } for(std::vector::iterator iter = m_FileWriters.begin(), endIter = m_FileWriters.end(); iter != endIter; ++iter) { delete *iter; } for(std::vector::iterator iter = m_FileIOs.begin(), endIter = m_FileIOs.end(); iter != endIter; ++iter) { delete *iter; } for(std::vector::iterator iter = m_LegacyWriters.begin(), endIter = m_LegacyWriters.end(); iter != endIter; ++iter) { delete *iter; } // The mitk::ModuleContext* argument of the Unload() method // will always be 0 for the Mitk library. It makes no sense // to use it at this stage anyway, since all libraries which // know about the module system have already been unloaded. // we need to close the internal service tracker of the // MimeTypeProvider class here. Otherwise it // would hold on to the ModuleContext longer than it is // actually valid. m_MimeTypeProviderReg.Unregister(); m_MimeTypeProvider->Stop(); + for (std::vector::const_iterator mimeTypeIter = m_DefaultMimeTypes.begin(), + iterEnd = m_DefaultMimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) + { + delete *mimeTypeIter; + } + m_ShaderRepositoryTracker->Close(); } void MitkCoreActivator::RegisterDefaultMimeTypes() { // Register some default mime-types - std::vector mimeTypes = mitk::IOMimeTypes::Get(); - for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), + std::vector mimeTypes = mitk::IOMimeTypes::Get(); + for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { m_DefaultMimeTypes.push_back(*mimeTypeIter); - m_Context->RegisterService(&m_DefaultMimeTypes.back()); + m_Context->RegisterService(m_DefaultMimeTypes.back()); } } void MitkCoreActivator::RegisterItkReaderWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); + for (std::list::iterator i = allobjects.begin(), endIter = allobjects.end(); i != endIter; ++i) { itk::ImageIOBase* io = dynamic_cast(i->GetPointer()); // NiftiImageIO does not provide a correct "SupportsDimension()" methods // and the supported read/write extensions are not ordered correctly if (dynamic_cast(io)) continue; + // Use a custom mime-type for GDCMImageIO below + if (dynamic_cast(i->GetPointer())) + { + // MITK provides its own DICOM reader (which internally uses GDCMImageIO). + continue; + } + if (io) { m_FileIOs.push_back(new mitk::ItkImageIO(io)); } else { MITK_WARN << "Error ImageIO factory did not return an ImageIOBase: " << ( *i )->GetNameOfClass(); } } - mitk::ItkImageIO* io = new mitk::ItkImageIO(mitk::CustomMimeType(mitk::IOMimeTypes::NIFTI_MIMETYPE_NAME()), - new FixedNiftiImageIO(), 0); - m_FileIOs.push_back(io); + FixedNiftiImageIO::Pointer itkNiftiIO = FixedNiftiImageIO::New(); + mitk::ItkImageIO* niftiIO = new mitk::ItkImageIO(mitk::CustomMimeType(mitk::IOMimeTypes::NIFTI_MIMETYPE_NAME()), + itkNiftiIO.GetPointer(), 0); + m_FileIOs.push_back(niftiIO); } void MitkCoreActivator::RegisterVtkReaderWriter() { m_FileIOs.push_back(new mitk::SurfaceVtkXmlIO()); m_FileIOs.push_back(new mitk::SurfaceStlIO()); m_FileIOs.push_back(new mitk::SurfaceVtkLegacyIO()); m_FileIOs.push_back(new mitk::ImageVtkXmlIO()); m_FileIOs.push_back(new mitk::ImageVtkLegacyIO()); } void MitkCoreActivator::RegisterLegacyWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("IOWriter"); for( std::list::iterator i = allobjects.begin(); i != allobjects.end(); ++i) { mitk::FileWriter::Pointer io = dynamic_cast(i->GetPointer()); if(io) { std::string description = std::string("Legacy ") + io->GetNameOfClass() + " Reader"; mitk::IFileWriter* writer = new mitk::LegacyFileWriterService(io, description); m_LegacyWriters.push_back(writer); } else { MITK_ERROR << "Error IOWriter override is not of type mitk::FileWriter: " << (*i)->GetNameOfClass() << std::endl; } } } -US_EXPORT_MODULE_ACTIVATOR(MitkCore, MitkCoreActivator) +US_EXPORT_MODULE_ACTIVATOR(MitkCoreActivator) // Call CppMicroservices initialization code at the end of the file. // This especially ensures that VTK object factories have already // been registered (VTK initialization code is injected by implicitly // include VTK header files at the top of this file). -US_INITIALIZE_MODULE("MitkCore", "MitkCore") +US_INITIALIZE_MODULE diff --git a/Core/Code/Internal/mitkCoreActivator.h b/Core/Code/Internal/mitkCoreActivator.h index 2e47d4e9ce..37a01eea4a 100644 --- a/Core/Code/Internal/mitkCoreActivator.h +++ b/Core/Code/Internal/mitkCoreActivator.h @@ -1,87 +1,87 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCOREACTIVATOR_H_ #define MITKCOREACTIVATOR_H_ // File IO #include #include #include #include #include #include #include #include #include #include // Micro Services #include #include #include #include #include /* * This is the module activator for the "Mitk" module. It registers core services * like ... */ class MitkCoreActivator : public us::ModuleActivator { public: void Load(us::ModuleContext* context); void Unload(us::ModuleContext* ); private: void HandleModuleEvent(const us::ModuleEvent moduleEvent); void RegisterDefaultMimeTypes(); void RegisterItkReaderWriter(); void RegisterVtkReaderWriter(); void RegisterLegacyWriter(); std::auto_ptr > m_ShaderRepositoryTracker; //mitk::RenderingManager::Pointer m_RenderingManager; std::auto_ptr m_PlanePositionManager; std::auto_ptr m_PropertyAliases; std::auto_ptr m_PropertyDescriptions; std::auto_ptr m_PropertyExtensions; std::auto_ptr m_PropertyFilters; std::auto_ptr m_MimeTypeProvider; // File IO std::vector m_FileReaders; std::vector m_FileWriters; std::vector m_FileIOs; std::vector m_LegacyWriters; - std::vector m_DefaultMimeTypes; + std::vector m_DefaultMimeTypes; us::ServiceRegistration m_MimeTypeProviderReg; us::ModuleContext* m_Context; }; #endif // MITKCOREACTIVATOR_H_ diff --git a/Core/Code/Internal/mitkDicomSeriesReaderService.cpp b/Core/Code/Internal/mitkDicomSeriesReaderService.cpp new file mode 100644 index 0000000000..c355574436 --- /dev/null +++ b/Core/Code/Internal/mitkDicomSeriesReaderService.cpp @@ -0,0 +1,134 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkDicomSeriesReaderService.h" + +#include +#include +#include +#include +#include + +#include + +namespace mitk { + +DicomSeriesReaderService::DicomSeriesReaderService() + : AbstractFileReader(CustomMimeType(IOMimeTypes::DICOM_MIMETYPE_NAME()), "MITK DICOM Reader") +{ + this->RegisterService(); +} + +std::vector > DicomSeriesReaderService::Read() +{ + std::vector result; + + const char* previousCLocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + std::locale previousCppLocale( std::cin.getloc() ); + std::locale l( "C" ); + std::cin.imbue(l); + + std::string fileName = this->GetLocalFileName(); + if ( DicomSeriesReader::IsPhilips3DDicom(fileName) ) + { + MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; + DataNode::Pointer node = DataNode::New(); + mitk::DicomSeriesReader::StringContainer stringvec; + stringvec.push_back(fileName); + if (DicomSeriesReader::LoadDicomSeries(stringvec, *node)) + { + BaseData::Pointer data = node->GetData(); + StringProperty::Pointer nameProp = StringProperty::New(itksys::SystemTools::GetFilenameName(fileName)); + data->GetPropertyList()->SetProperty("name", nameProp); + result.push_back(data); + } + setlocale(LC_NUMERIC, previousCLocale); + std::cin.imbue(previousCppLocale); + return result; + + } + + DicomSeriesReader::FileNamesGrouping imageBlocks = DicomSeriesReader::GetSeries(itksys::SystemTools::GetFilenamePath(fileName), + true); // true = group gantry tilt images + const unsigned int size = imageBlocks.size(); + + ProgressBar::GetInstance()->AddStepsToDo(size); + ProgressBar::GetInstance()->Progress(); + + unsigned int outputIndex = 0u; + const DicomSeriesReader::FileNamesGrouping::const_iterator n_end = imageBlocks.end(); + + for (DicomSeriesReader::FileNamesGrouping::const_iterator n_it = imageBlocks.begin(); n_it != n_end; ++n_it) + { + const std::string &uid = n_it->first; + DataNode::Pointer node = DataNode::New(); + + const DicomSeriesReader::ImageBlockDescriptor& imageBlockDescriptor( n_it->second ); + + MITK_INFO << "--------------------------------------------------------------------------------"; + MITK_INFO << "DicomSeriesReader: Loading DICOM series " << outputIndex << ": Series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << std::endl; + MITK_INFO << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; + MITK_INFO << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); + MITK_INFO << " reader support: " << DicomSeriesReader::ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); + MITK_INFO << " pixel spacing type: " << DicomSeriesReader::PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ); + MITK_INFO << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); + MITK_INFO << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); + MITK_INFO << "--------------------------------------------------------------------------------"; + + if (DicomSeriesReader::LoadDicomSeries(n_it->second.GetFilenames(), *node, true, true, true)) + { + BaseData::Pointer data = node->GetData(); + PropertyList::Pointer dataProps = data->GetPropertyList(); + + std::string nodeName(uid); + std::string studyDescription; + if ( dataProps->GetStringProperty( "dicom.study.StudyDescription", studyDescription ) ) + { + nodeName = studyDescription; + std::string seriesDescription; + if ( dataProps->GetStringProperty( "dicom.series.SeriesDescription", seriesDescription ) ) + { + nodeName += "/" + seriesDescription; + } + } + + StringProperty::Pointer nameProp = StringProperty::New(nodeName); + data->SetProperty("name", nameProp); + + result.push_back(data); + ++outputIndex; + } + else + { + MITK_ERROR << "DicomSeriesReader: Skipping series " << outputIndex << " due to some unspecified error..." << std::endl; + } + + ProgressBar::GetInstance()->Progress(); + } + + setlocale(LC_NUMERIC, previousCLocale); + std::cin.imbue(previousCppLocale); + + return result; +} + +DicomSeriesReaderService* DicomSeriesReaderService::Clone() const +{ + return new DicomSeriesReaderService(*this); +} + +} diff --git a/Modules/SceneSerialization/mitkSceneDataNodeReader.cpp b/Core/Code/Internal/mitkDicomSeriesReaderService.h similarity index 55% rename from Modules/SceneSerialization/mitkSceneDataNodeReader.cpp rename to Core/Code/Internal/mitkDicomSeriesReaderService.h index 6453c33574..ef86a0255a 100644 --- a/Modules/SceneSerialization/mitkSceneDataNodeReader.cpp +++ b/Core/Code/Internal/mitkDicomSeriesReaderService.h @@ -1,37 +1,39 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ +#ifndef MITKDICOMSERIESREADERSERVICE_H +#define MITKDICOMSERIESREADERSERVICE_H -#include "mitkSceneDataNodeReader.h" - -#include "mitkSceneIO.h" +#include namespace mitk { -int mitk::SceneDataNodeReader::Read(const std::string &fileName, mitk::DataStorage &storage) +class DicomSeriesReaderService : public AbstractFileReader { - int n = 0; - if (fileName.substr(fileName.size()-5) == ".mitk") - { - SceneIO::Pointer sceneIO = SceneIO::New(); - int oldSize = storage.GetAll()->Size(); - sceneIO->LoadScene(fileName, &storage, false); - n = storage.GetAll()->Size() - oldSize; - } - return n; -} +public: + DicomSeriesReaderService(); + + using AbstractFileReader::Read; + virtual std::vector > Read(); + +private: + + virtual DicomSeriesReaderService* Clone() const; +}; } + +#endif // MITKDICOMSERIESREADERSERVICE_H diff --git a/Core/Code/Internal/mitkFileReaderWriterBase.cpp b/Core/Code/Internal/mitkFileReaderWriterBase.cpp index cf0eb215f7..5dd58f3b17 100644 --- a/Core/Code/Internal/mitkFileReaderWriterBase.cpp +++ b/Core/Code/Internal/mitkFileReaderWriterBase.cpp @@ -1,261 +1,261 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFileReaderWriterBase.h" #include "mitkLogMacros.h" #include "mitkCoreServices.h" #include "mitkIMimeTypeProvider.h" #include "mitkIOMimeTypes.h" #include #include namespace mitk { FileReaderWriterBase::FileReaderWriterBase() : m_Ranking(0) , m_MimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".") { } FileReaderWriterBase::~FileReaderWriterBase() { this->UnregisterMimeType(); } FileReaderWriterBase::FileReaderWriterBase(const FileReaderWriterBase& other) : m_Description(other.m_Description) , m_Ranking(other.m_Ranking) , m_MimeTypePrefix(other.m_MimeTypePrefix) , m_Options(other.m_Options) , m_DefaultOptions(other.m_DefaultOptions) - , m_CustomMimeType(other.m_CustomMimeType) + , m_CustomMimeType(other.m_CustomMimeType->Clone()) { } FileReaderWriterBase::Options FileReaderWriterBase::GetOptions() const { Options options = m_Options; options.insert(m_DefaultOptions.begin(), m_DefaultOptions.end()); return options; } us::Any FileReaderWriterBase::GetOption(const std::string& name) const { Options::const_iterator iter = m_Options.find(name); if (iter != m_Options.end()) { return iter->second; } iter = m_DefaultOptions.find(name); if (iter != m_DefaultOptions.end()) { return iter->second; } return us::Any(); } void FileReaderWriterBase::SetOptions(const FileReaderWriterBase::Options& options) { for(Options::const_iterator iter = options.begin(), iterEnd = options.end(); iter != iterEnd; ++iter) { this->SetOption(iter->first, iter->second); } } void FileReaderWriterBase::SetOption(const std::string& name, const us::Any& value) { if (m_DefaultOptions.find(name) == m_DefaultOptions.end()) { MITK_WARN << "Ignoring unknown IFileReader option '" << name << "'"; } else { if (value.Empty()) { // an empty Any signals 'reset to default value' m_Options.erase(name); } else { m_Options[name] = value; } } } void FileReaderWriterBase::SetDefaultOptions(const FileReaderWriterBase::Options& defaultOptions) { m_DefaultOptions = defaultOptions; } FileReaderWriterBase::Options FileReaderWriterBase::GetDefaultOptions() const { return m_DefaultOptions; } void FileReaderWriterBase::SetRanking(int ranking) { m_Ranking = ranking; } int FileReaderWriterBase::GetRanking() const { return m_Ranking; } void FileReaderWriterBase::SetMimeType(const CustomMimeType& mimeType) { - m_CustomMimeType = mimeType; + m_CustomMimeType.reset(mimeType.Clone()); } -CustomMimeType FileReaderWriterBase::GetMimeType() const +const CustomMimeType* FileReaderWriterBase::GetMimeType() const { - return m_CustomMimeType; + return m_CustomMimeType.get(); } -CustomMimeType& FileReaderWriterBase::GetMimeType() +CustomMimeType* FileReaderWriterBase::GetMimeType() { - return m_CustomMimeType; + return m_CustomMimeType.get(); } MimeType FileReaderWriterBase::GetRegisteredMimeType() const { MimeType result; if (!m_MimeTypeReg) { - if (!m_CustomMimeType.GetName().empty()) + if (!m_CustomMimeType->GetName().empty()) { CoreServicePointer mimeTypeProvider( CoreServices::GetMimeTypeProvider(us::GetModuleContext())); - return mimeTypeProvider->GetMimeTypeForName(m_CustomMimeType.GetName()); + return mimeTypeProvider->GetMimeTypeForName(m_CustomMimeType->GetName()); } return result; } us::ServiceReferenceU reference = m_MimeTypeReg.GetReference(); try { int rank = 0; us::Any rankProp = reference.GetProperty(us::ServiceConstants::SERVICE_RANKING()); if (!rankProp.Empty()) { rank = us::any_cast(rankProp); } long id = us::any_cast(reference.GetProperty(us::ServiceConstants::SERVICE_ID())); - result = MimeType(m_CustomMimeType, rank, id); + result = MimeType(*m_CustomMimeType, rank, id); } catch (const us::BadAnyCastException& e) { MITK_WARN << "Unexpected exception: " << e.what(); } return result; } void FileReaderWriterBase::SetMimeTypePrefix(const std::string& prefix) { m_MimeTypePrefix = prefix; } std::string FileReaderWriterBase::GetMimeTypePrefix() const { return m_MimeTypePrefix; } void FileReaderWriterBase::SetDescription(const std::string& description) { m_Description = description; } std::string FileReaderWriterBase::GetDescription() const { return m_Description; } void FileReaderWriterBase::AddProgressCallback(const FileReaderWriterBase::ProgressCallback& callback) { m_ProgressMessage += callback; } void FileReaderWriterBase::RemoveProgressCallback(const FileReaderWriterBase::ProgressCallback& callback) { m_ProgressMessage -= callback; } us::ServiceRegistration FileReaderWriterBase::RegisterMimeType(us::ModuleContext* context) { if (context == NULL) throw std::invalid_argument("The context argument must not be NULL."); CoreServicePointer mimeTypeProvider(CoreServices::GetMimeTypeProvider(context)); - const std::vector extensions = m_CustomMimeType.GetExtensions(); + const std::vector extensions = m_CustomMimeType->GetExtensions(); // If the mime type name is set and the list of extensions is empty, // look up the mime type in the registry and print a warning if // there is none - if (!m_CustomMimeType.GetName().empty() && extensions.empty()) + if (!m_CustomMimeType->GetName().empty() && extensions.empty()) { - if(!mimeTypeProvider->GetMimeTypeForName(m_CustomMimeType.GetName()).IsValid()) + if(!mimeTypeProvider->GetMimeTypeForName(m_CustomMimeType->GetName()).IsValid()) { - MITK_WARN << "Registering a MITK reader or writer with an unknown MIME type " << m_CustomMimeType.GetName(); + MITK_WARN << "Registering a MITK reader or writer with an unknown MIME type " << m_CustomMimeType->GetName(); } return m_MimeTypeReg; } // If the mime type name and extensions list is empty, print a warning - if(m_CustomMimeType.GetName().empty() && extensions.empty()) + if(m_CustomMimeType->GetName().empty() && extensions.empty()) { MITK_WARN << "Trying to register a MITK reader or writer with an empty mime type name and empty extension list."; return m_MimeTypeReg; } // extensions is not empty - if(m_CustomMimeType.GetName().empty()) + if(m_CustomMimeType->GetName().empty()) { // Create a synthetic mime type name from the // first extension in the list - m_CustomMimeType.SetName(m_MimeTypePrefix + extensions.front()); + m_CustomMimeType->SetName(m_MimeTypePrefix + extensions.front()); } // Register a new mime type //us::ServiceProperties props; //props["name"] = m_CustomMimeType.GetName(); //props["extensions"] = m_CustomMimeType.GetExtensions(); - m_MimeTypeReg = context->RegisterService(&m_CustomMimeType); + m_MimeTypeReg = context->RegisterService(m_CustomMimeType.get()); return m_MimeTypeReg; } void FileReaderWriterBase::UnregisterMimeType() { if (m_MimeTypeReg) { try { m_MimeTypeReg.Unregister(); } catch (const std::logic_error&) { // service already unregistered } } } } diff --git a/Core/Code/Internal/mitkFileReaderWriterBase.h b/Core/Code/Internal/mitkFileReaderWriterBase.h index bfba2751e3..b705cb7d37 100644 --- a/Core/Code/Internal/mitkFileReaderWriterBase.h +++ b/Core/Code/Internal/mitkFileReaderWriterBase.h @@ -1,113 +1,115 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKFILEREADERWRITERBASE_H #define MITKFILEREADERWRITERBASE_H #include #include #include #include #include +#include + namespace mitk { class FileReaderWriterBase { public: typedef std::map Options; typedef mitk::MessageAbstractDelegate1 ProgressCallback; FileReaderWriterBase(); virtual ~FileReaderWriterBase(); Options GetOptions() const; us::Any GetOption(const std::string &name) const; void SetOptions(const Options& options); void SetOption(const std::string& name, const us::Any& value); void SetDefaultOptions(const Options& defaultOptions); Options GetDefaultOptions() const; /** * \brief Set the service ranking for this file reader. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which reader to use if several * equivalent readers have been found. * It may be used to replace a default reader from MITK in your own project. * E.g. if you want to use your own reader for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetRanking(int ranking); int GetRanking() const; void SetMimeType(const CustomMimeType& mimeType); - CustomMimeType GetMimeType() const; - CustomMimeType& GetMimeType(); + const CustomMimeType* GetMimeType() const; + CustomMimeType* GetMimeType(); MimeType GetRegisteredMimeType() const; void SetMimeTypePrefix(const std::string& prefix); std::string GetMimeTypePrefix() const; void SetDescription(const std::string& description); std::string GetDescription() const; void AddProgressCallback(const ProgressCallback& callback); void RemoveProgressCallback(const ProgressCallback& callback); us::ServiceRegistration RegisterMimeType(us::ModuleContext* context); void UnregisterMimeType(); protected: FileReaderWriterBase(const FileReaderWriterBase& other); std::string m_Description; int m_Ranking; std::string m_MimeTypePrefix; /** * \brief Options supported by this reader. Set sensible default values! * * Can be left emtpy if no special options are required. */ Options m_Options; Options m_DefaultOptions; //us::PrototypeServiceFactory* m_PrototypeFactory; Message1 m_ProgressMessage; - CustomMimeType m_CustomMimeType; + std::auto_ptr m_CustomMimeType; us::ServiceRegistration m_MimeTypeReg; private: // purposely not implemented FileReaderWriterBase& operator=(const FileReaderWriterBase& other); }; } #endif // MITKFILEREADERWRITERBASE_H diff --git a/Core/Code/Internal/mitkItkImageIO.cpp b/Core/Code/Internal/mitkItkImageIO.cpp index b1965014fe..2080c7f555 100644 --- a/Core/Code/Internal/mitkItkImageIO.cpp +++ b/Core/Code/Internal/mitkItkImageIO.cpp @@ -1,507 +1,509 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include namespace mitk { ItkImageIO::ItkImageIO(const ItkImageIO& other) : AbstractFileIO(other) , m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string& imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); + extensions.push_back("ima"); + extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()) , m_ImageIO(imageIO) { if (m_ImageIO.IsNull() ) { mitkThrow() << "ITK ImageIOBase argument must not be NULL"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for(std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin()+1, iter->end()); } customReaderMimeType.AddExtension(extension); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for(std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin()+1, iter->end()); } customWriterMimeType.AddExtension(extension); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType& mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()) , m_ImageIO(imageIO) { if (m_ImageIO.IsNull() ) { mitkThrow() << "ITK ImageIOBase argument must not be NULL"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } std::vector ItkImageIO::Read() { std::vector result; const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName( path ); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if ( ndim < MINDIM || ndim > MAXDIM ) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion( ndim ); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[ MAXDIM ]; dimensions[ 0 ] = 0; dimensions[ 1 ] = 0; dimensions[ 2 ] = 0; dimensions[ 3 ] = 0; ScalarType spacing[ MAXDIM ]; spacing[ 0 ] = 1.0f; spacing[ 1 ] = 1.0f; spacing[ 2 ] = 1.0f; spacing[ 3 ] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for ( i = 0; i < ndim ; ++i ) { ioStart[ i ] = 0; ioSize[ i ] = m_ImageIO->GetDimensions( i ); if(iGetDimensions( i ); spacing[ i ] = m_ImageIO->GetSpacing( i ); if(spacing[ i ] <= 0) spacing[ i ] = 1.0f; } if(i<3) { origin[ i ] = m_ImageIO->GetOrigin( i ); } } ioRegion.SetSize( ioSize ); ioRegion.SetIndex( ioStart ); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion( ioRegion ); void* buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read( buffer ); image->Initialize( MakePixelType(m_ImageIO), ndim, dimensions ); image->SetImportChannel( buffer, 0, Image::ManageMemory ); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3? 3 : ndim); for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false,false,false); MITK_INFO << slicedGeometry->GetCornerPoint(true,true,true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = NULL; MITK_INFO << "number of image components: "<< image->GetPixelType().GetNumberOfComponents() << std::endl; const itk::MetaDataDictionary& dictionary = m_ImageIO->GetMetaDataDictionary(); for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { std::string key = std::string("meta.") + iter->first; if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); image->SetProperty(key.c_str(), mitk::StringProperty::New(value)); } } MITK_INFO << "...finished!" << std::endl; try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const mitk::Image* image = dynamic_cast(this->GetInput()); if (image == NULL) { mitkThrow() << "Cannot write non-image data"; } struct LocaleSwitch { LocaleSwitch(const std::string& newLocale) : m_OldLocale(std::setlocale(LC_ALL, NULL)) , m_NewLocale(newLocale) { if (m_OldLocale == NULL) { m_OldLocale = ""; } else if (m_NewLocale != m_OldLocale) { // set the locale if (std::setlocale(LC_ALL, m_NewLocale.c_str()) == NULL) { MITK_INFO << "Could not set locale " << m_NewLocale; m_OldLocale = NULL; } } } ~LocaleSwitch() { if (m_OldLocale != NULL && std::setlocale(LC_ALL, m_OldLocale) == NULL) { MITK_INFO << "Could not reset locale " << m_OldLocale; } } private: const char* m_OldLocale; const std::string m_NewLocale; }; // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int* const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents( pixelType.GetNumberOfComponents() ); itk::ImageIORegion ioRegion( dimension ); for(unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for(unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } //use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // ***** Remove const_cast after bug 17952 is fixed **** ImageReadAccessor imageAccess(const_cast(image)); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception& e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const Image* image = dynamic_cast(this->GetInput()); if (image == NULL || !m_ImageIO->SupportsDimension(image->GetDimension())) { return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO* ItkImageIO::IOClone() const { return new ItkImageIO(*this); } } diff --git a/Core/Code/Internal/mitkMimeTypeProvider.cpp b/Core/Code/Internal/mitkMimeTypeProvider.cpp index 0ca97fe3d9..df02eb26c7 100644 --- a/Core/Code/Internal/mitkMimeTypeProvider.cpp +++ b/Core/Code/Internal/mitkMimeTypeProvider.cpp @@ -1,198 +1,186 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMimeTypeProvider.h" #include "mitkLogMacros.h" #include #include #include #ifdef _MSC_VER #pragma warning(disable:4503) // decorated name length exceeded, name was truncated #pragma warning(disable:4355) #endif namespace mitk { MimeTypeProvider::MimeTypeProvider() : m_Tracker(NULL) { } MimeTypeProvider::~MimeTypeProvider() { delete m_Tracker; } void MimeTypeProvider::Start() { if (m_Tracker == NULL) { m_Tracker = new us::ServiceTracker(us::GetModuleContext(), this); } m_Tracker->Open(); } void MimeTypeProvider::Stop() { m_Tracker->Close(); } std::vector MimeTypeProvider::GetMimeTypes() const { std::vector result; for (std::map::const_iterator iter = m_NameToMimeType.begin(), end = m_NameToMimeType.end(); iter != end; ++iter) { result.push_back(iter->second); } return result; } std::vector MimeTypeProvider::GetMimeTypesForFile(const std::string& filePath) const -{ - // For now, just use the file extension to look-up the registered mime-types. - std::string extension = itksys::SystemTools::GetFilenameExtension(filePath); - if (!extension.empty()) - { - extension = extension.substr(1, extension.size()-1); - } - return this->GetMimeTypesForExtension(extension); -} - -std::vector MimeTypeProvider::GetMimeTypesForExtension(const std::string& extension) const { std::vector result; for (std::map::const_iterator iter = m_NameToMimeType.begin(), iterEnd = m_NameToMimeType.end(); iter != iterEnd; ++iter) { - const std::vector extensions = iter->second.GetExtensions(); - if (std::find(extensions.begin(), extensions.end(), extension) != extensions.end()) + if (iter->second.AppliesTo(filePath)) { result.push_back(iter->second); } } std::sort(result.begin(), result.end()); std::reverse(result.begin(), result.end()); return result; } std::vector MimeTypeProvider::GetMimeTypesForCategory(const std::string& category) const { std::vector result; for (std::map::const_iterator iter = m_NameToMimeType.begin(), end = m_NameToMimeType.end(); iter != end; ++iter) { if (iter->second.GetCategory() == category) { result.push_back(iter->second); } } return result; } MimeType MimeTypeProvider::GetMimeTypeForName(const std::string& name) const { std::map::const_iterator iter = m_NameToMimeType.find(name); if (iter != m_NameToMimeType.end()) return iter->second; return MimeType(); } std::vector MimeTypeProvider::GetCategories() const { std::vector result; for (std::map::const_iterator iter = m_NameToMimeType.begin(), end = m_NameToMimeType.end(); iter != end; ++iter) { std::string category = iter->second.GetCategory(); if (!category.empty()) { result.push_back(category); } } std::sort(result.begin(), result.end()); result.erase(std::unique(result.begin(), result.end()), result.end()); return result; } MimeTypeProvider::TrackedType MimeTypeProvider::AddingService(const ServiceReferenceType& reference) { MimeType result = this->GetMimeType(reference); if (result.IsValid()) { std::string name = result.GetName(); m_NameToMimeTypes[name].insert(result); // get the highest ranked mime-type m_NameToMimeType[name] = *(m_NameToMimeTypes[name].rbegin()); } return result; } void MimeTypeProvider::ModifiedService(const ServiceReferenceType& /*reference*/, TrackedType /*mimetype*/) { // should we track changes in the ranking property? } void MimeTypeProvider::RemovedService(const ServiceReferenceType& /*reference*/, TrackedType mimeType) { std::string name = mimeType.GetName(); std::set& mimeTypes = m_NameToMimeTypes[name]; mimeTypes.erase(mimeType); if (mimeTypes.empty()) { m_NameToMimeTypes.erase(name); m_NameToMimeType.erase(name); } else { // get the highest ranked mime-type m_NameToMimeType[name] = *(mimeTypes.rbegin()); } } MimeType MimeTypeProvider::GetMimeType(const ServiceReferenceType& reference) const { MimeType result; if (!reference) return result; CustomMimeType* mimeType = us::GetModuleContext()->GetService(reference); if (mimeType != NULL) { try { int rank = 0; us::Any rankProp = reference.GetProperty(us::ServiceConstants::SERVICE_RANKING()); if (!rankProp.Empty()) { rank = us::any_cast(rankProp); } long id = us::any_cast(reference.GetProperty(us::ServiceConstants::SERVICE_ID())); result = MimeType(*mimeType, rank, id); } catch (const us::BadAnyCastException& e) { MITK_WARN << "Unexpected exception: " << e.what(); } us::GetModuleContext()->UngetService(reference); } return result; } } diff --git a/Core/Code/Internal/mitkMimeTypeProvider.h b/Core/Code/Internal/mitkMimeTypeProvider.h index 59d1152e08..9e7de95120 100644 --- a/Core/Code/Internal/mitkMimeTypeProvider.h +++ b/Core/Code/Internal/mitkMimeTypeProvider.h @@ -1,86 +1,85 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKMIMETYPEPROVIDER_H #define MITKMIMETYPEPROVIDER_H #include "mitkIMimeTypeProvider.h" #include "mitkCustomMimeType.h" #include "usServiceTracker.h" #include "usServiceTrackerCustomizer.h" #include namespace mitk { struct MimeTypeTrackerTypeTraits : public us::TrackedTypeTraitsBase { typedef MimeType TrackedType; static bool IsValid(const TrackedType& t) { return t.IsValid(); } static TrackedType DefaultValue() { return TrackedType(); } static void Dispose(TrackedType& /*t*/) { } }; class MimeTypeProvider : public IMimeTypeProvider, private us::ServiceTrackerCustomizer { public: MimeTypeProvider(); ~MimeTypeProvider(); void Start(); void Stop(); virtual std::vector GetMimeTypes() const; virtual std::vector GetMimeTypesForFile(const std::string& filePath) const; - virtual std::vector GetMimeTypesForExtension(const std::string& extension) const; virtual std::vector GetMimeTypesForCategory(const std::string& category) const; virtual MimeType GetMimeTypeForName(const std::string& name) const; virtual std::vector GetCategories() const; private: virtual TrackedType AddingService(const ServiceReferenceType& reference); virtual void ModifiedService(const ServiceReferenceType& reference, TrackedType service); virtual void RemovedService(const ServiceReferenceType& reference, TrackedType service); MimeType GetMimeType(const ServiceReferenceType& reference) const; us::ServiceTracker* m_Tracker; typedef std::map > MapType; MapType m_NameToMimeTypes; std::map m_NameToMimeType; }; } #endif // MITKMIMETYPEPROVIDER_H diff --git a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp index 35e3f12605..96f122d406 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1081 +1,1080 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //MITK #include #include #include #include #include #include #include #include #include #include //#include #include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" //MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" //VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //ITK #include #include mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { //The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, //in order to delete the images from the 3D RW. this->InvokeEvent( itk::DeleteEvent() ); } //set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer* renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); //Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct //plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); //These two points define the axes of the plane in combination with the origin. //Point 1 is the x-axis and point 2 the y-axis. //Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1] , planeBounds[2], depth); //P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); //P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer* renderer) { //get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; //Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange*0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty( "layer", layer, renderer); //add the layer property for each image to render images with a higher layer on top of the others depth += layer*10; //*10: keep some room for each image (e.g. for QBalls in between) if(depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image* mitk::ImageVtkMapper2D::GetInput( void ) { return static_cast< const mitk::Image * >( GetDataNode()->GetData() ); } vtkProp* mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer* renderer) { //return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); mitk::DataNode* datanode = this->GetDataNode(); if ( input == NULL || input->IsInitialized() == false ) { return; } //check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if( ( worldGeometry == NULL ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() )) { return; } input->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if ( !RenderingGeometryIntersectsImage( worldGeometry, input->GetSlicedGeometry() ) ) { // set image to NULL, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = NULL; localStorage->m_Mapper->SetInputData( localStorage->m_EmptyPolyData ); return; } //set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(input); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep( this->GetTimestep() ); //set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( input->GetTimeGeometry()->GetGeometryForTimeStep( this->GetTimestep() ) ); //is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ( (input->GetDimension() >= 3) && (input->GetDimension(2) > 1) ) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty( resliceInterpolationProperty, "reslice interpolation", renderer ); int interpolationMode = VTK_RESLICE_NEAREST; if ( resliceInterpolationProperty != NULL ) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch ( interpolationMode ) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } //set the vtk output property to true, makes sure that no unneeded mitk image convertion //is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); //Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if( input->GetPixelType().GetNumberOfComponents() == 1 ) // for now only single component are allowed { DataNode *dn=renderer->GetCurrentWorldPlaneGeometryNode(); if(dn) { ResliceMethodProperty *resliceMethodEnumProperty=0; if( dn->GetProperty( resliceMethodEnumProperty, "reslice.thickslices", renderer ) && resliceMethodEnumProperty ) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty=0; if( dn->GetProperty( intProperty, "reslice.thickslices.num", renderer ) && intProperty ) { thickSlicesNum = intProperty->GetValue(); if(thickSlicesNum < 1) thickSlicesNum=1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast< const PlaneGeometry * >( worldGeometry ); if(thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(worldGeometry); if(abstractGeometry != NULL) normal = abstractGeometry->GetPlane()->GetNormal(); else{ if ( planeGeometry != NULL ){ normal = planeGeometry->GetNormal(); } else return; //no fitting geometry set } normal.Normalize(); input->GetTimeGeometry()->GetGeometryForTimeStep( this->GetTimestep() )->WorldToIndex( normal, normInIndex ); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality( 3 ); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection( -thickSlicesNum, 0+thickSlicesNum ); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode( thickSlicesMode-1 ); localStorage->m_TSFilter->SetInputData( localStorage->m_Reslicer->GetVtkOutput() ); //vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { //this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality( 2 ); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection( 0, 0 ); localStorage->m_Reslicer->Modified(); //start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) //this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for ( int i = 0; i < 6; ++i ) { sliceBounds[i] = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); //get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for ( int i = 0; i < 6; ++i ) { textureClippingBounds[i] = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds( input->GetGeometry(), planeGeometry, textureClippingBounds ); textureClippingBounds[0] = static_cast< int >( textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5 ); textureClippingBounds[1] = static_cast< int >( textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5 ); textureClippingBounds[2] = static_cast< int >( textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5 ); textureClippingBounds[3] = static_cast< int >( textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5 ); //clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } //get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); //get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty( "binary", binary, renderer ); if(binary) //binary image { datanode->GetBoolProperty( "outline binary", binaryOutline, renderer ); if(binaryOutline) //contour rendering { if ( input->GetPixelType().GetBpe() <= 8 ) { //generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); float binaryOutlineWidth(1.0); if ( datanode->GetFloatProperty( "outline width", binaryOutlineWidth, renderer ) ) { if ( localStorage->m_Actors->GetNumberOfPaths() > 1 ) { float binaryOutlineShadowWidth(1.5); datanode->GetFloatProperty( "outline shadow width", binaryOutlineShadowWidth, renderer ); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty()->SetLineWidth( binaryOutlineWidth * binaryOutlineShadowWidth ); } localStorage->m_Actor->GetProperty()->SetLineWidth( binaryOutlineWidth ); } } else { binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be (un)signed char. Outline does not work on other pixel types!"; } } else //standard binary image { if(numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity( renderer ); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { //connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty( "texture interpolation", textureInterpolation, renderer ); //set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor( renderer ); vtkActor* contourShadowActor = dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if(binary && binaryOutline) //connect the mapper with the polyData which contains the lines { //We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(NULL); //no texture for contours bool binaryOutlineShadow( false ); datanode->GetBoolProperty( "outline binary shadow", binaryOutlineShadow, renderer ); if ( binaryOutlineShadow ) contourShadowActor->SetVisibility( true ); else contourShadowActor->SetVisibility( false ); } else { //Connect the mapper with the input texture. This is the standard case. //setup the textured plane this->GeneratePlane( renderer, sliceBounds ); //set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); //set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility( false ); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage( renderer ); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow( levelWindow, renderer, "levelwindow" ); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange( levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound() ); mitk::LevelWindow opacLevelWindow; if( this->GetDataNode()->GetLevelWindow( opacLevelWindow, renderer, "opaclevelwindow" ) ) { //pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { //no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor( mitk::BaseRenderer* renderer ) { LocalStorage *localStorage = this->GetLocalStorage( renderer ); float rgb[3]= { 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if(binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.hoveringcolor", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } else { GetDataNode()->GetColor( rgb, renderer, "color" ); } } if(binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.selectedcolor", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if(!binary || (!hover && !selected)) { GetDataNode()->GetColor( rgb, renderer, "color" ); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; //conversion to double for VTK dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if ( localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1 ) { float rgb[3]= { 1.0f, 1.0f, 1.0f }; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("outline binary shadow color", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; //conversion to double for VTK dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) )->GetProperty()->SetColor(rgbConv); } } void mitk::ImageVtkMapper2D::ApplyOpacity( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = this->GetLocalStorage( renderer ); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity( opacity, renderer, "opacity" ); //set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if ( localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1 ) { dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) )->GetProperty()->SetOpacity(opacity); } } void mitk::ImageVtkMapper2D::ApplyRenderingMode( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty( "binary", binary, renderer ); if(binary) // is it a binary image? { //for binary images, we always use our default LuT and map every value to (0,1) //the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { //all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty( "Image Rendering.Mode", renderer )); if(mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch(renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable( renderer ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction( renderer ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable( renderer ); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction( renderer ); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable( renderer ); this->ApplyLevelWindow( renderer ); break; } } //we apply color for all images (including binaries). this->ApplyColor( renderer ); } void mitk::ImageVtkMapper2D::ApplyLookuptable( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable* usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if( lookupTableProp.IsNotNull() ) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. //A default (rainbow) lookup table will be used. //Here have to do nothing. Warning for the user has been removed, due to unwanted console output //in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Transfer Function",renderer )); if( transferFunctionProp.IsNull() ) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); //pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction(transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer* renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) { return; } mitk::Image* data = const_cast( this->GetInput() ); if ( data == NULL ) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep( renderer ); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->CountTimeSteps() == 0 ) || ( !dataTimeGeometry->IsValidTimeStep( this->GetTimestep() ) ) ) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //check if something important has changed and we need to rerender if ( (localStorage->m_LastUpdateTime < node->GetMTime()) //was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) //Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { this->GenerateDataForRenderer( renderer ); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty( "depthOffset", mitk::FloatProperty::New( 0.0 ), renderer, overwrite ); node->AddProperty( "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); node->AddProperty( "outline binary shadow", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline binary shadow color", ColorProperty::New(0.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "outline shadow width", mitk::FloatProperty::New( 1.5 ), renderer, overwrite ); if(image->IsRotated()) node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC) ); else node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); node->AddProperty( "texture interpolation", mitk::BoolProperty::New( false ) ); node->AddProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); node->AddProperty( "bounding box", mitk::BoolProperty::New( false ) ); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty( "Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if ( node->GetStringProperty( "dicom.pixel.PhotometricInterpretation", photometricInterpretation ) ) { // modality provided by DICOM or other reader if ( photometricInterpretation.find("MONOCHROME1") != std::string::npos ) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty( "LookupTable", mitkLutProp ); renderingModeProperty->SetValue( mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR ); // USE lookuptable - } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if ( ! node->GetBoolProperty("binary", isBinaryImage) ) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2)/2); sliceSelector->SetTimeNr(image->GetDimension(3)/2); sliceSelector->SetChannelNr(image->GetDimension(4)/2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if ( centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized() ) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = ( maxValue == min2ndValue && minValue == max2ndValue ); } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty( "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,1.0,1.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "QBallImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } } if(image.IsNotNull() && image->IsInitialized()) { if((overwrite) || (node->GetProperty("levelwindow", renderer)==NULL)) { /* initialize level/window from DICOM tags */ std::string sLevel; std::string sWindow; if ( image->GetPropertyList()->GetStringProperty( "dicom.voilut.WindowCenter", sLevel ) && image->GetPropertyList()->GetStringProperty( "dicom.voilut.WindowWidth", sWindow ) ) { float level = atof( sLevel.c_str() ); float window = atof( sWindow.c_str() ); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if ( image->GetPropertyList()->GetStringProperty( "dicom.series.SmallestPixelValueInSeries", sSmallestPixelValueInSeries ) && image->GetPropertyList()->GetStringProperty( "dicom.series.LargestPixelValueInSeries", sLargestPixelValueInSeries ) ) { float smallestPixelValueInSeries = atof( sSmallestPixelValueInSeries.c_str() ); float largestPixelValueInSeries = atof( sLargestPixelValueInSeries.c_str() ); contrast.SetRangeMinMax( smallestPixelValueInSeries-1, largestPixelValueInSeries+1 ); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto( static_cast(node->GetData()), false, true ); // we need this as a fallback } contrast.SetLevelWindow( level, window, true ); node->SetProperty( "levelwindow", LevelWindowProperty::New( contrast ), renderer ); } } if(((overwrite) || (node->GetProperty("opaclevelwindow", renderer)==NULL)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR) ) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0,255); opaclevwin.SetWindowBounds(0,255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty( "opaclevelwindow", prop, renderer ); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage* mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer* renderer ){ LocalStorage* localStorage = this->GetLocalStorage(renderer); //get the min and max index values of each direction int* extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int* dims = localStorage->m_ReslicedImage->GetDimensions(); //dimensions of the image int line = dims[0]; //how many pixels per line? int x = xMin; //pixel index x int y = yMin; //pixel index y char* currentPixel; //get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); //the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); //the lines to connect the points // We take the pointer to the first pixel of the image currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer() ); while (y <= yMax) { //if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { //check in which direction a line is necessary //a line is added if the neighbor of the current pixel has the value 0 //and if the pixel is located at the edge of the image //if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel-line) == 0) { //x direction - bottom edge of the pixel //add the 2 points vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); //add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel+line) == 0) { //x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the first pixel vvvvv if ( (x > xMin || y > yMin) && *(currentPixel-1) == 0) { //y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the last pixel vvvvv if ( (y < yMax || (x < xMax) ) && *(currentPixel+1) == 0) { //y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ //if vvvvv left edge of image vvvvv if (x == xMin) { //draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv right edge of image vvvvv if (x == xMax) { //draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv bottom edge of image vvvvv if (y == yMin) { //draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv top edge of image vvvvv if (y == yMax) { //draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } }//end if currentpixel is set x++; if (x > xMax) { //reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; }//end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer* renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); //transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); //transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0); if ( localStorage->m_Actors->GetNumberOfPaths() > 1 ) { vtkActor* secondaryActor = dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) ); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage( const PlaneGeometry* renderingGeometry, SlicedGeometry3D* imageGeometry ) { // if either one of the two geometries is NULL we return true // for safety reasons if ( renderingGeometry == NULL || imageGeometry == NULL ) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance( imageGeometry->GetCornerPoint( 0 ) ); for( int i=1; i<8; i++ ) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint( i ); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance( cornerPoint ); // if it has not the same signing as the distance of the first point if ( initialDistance * distance < 0 ) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); //Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); //the following actions are always the same and thus can be performed //in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); //built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); //do not repeat the texture (the image) m_Texture->RepeatOff(); //set the mapper for the actor m_Actor->SetMapper( m_Mapper ); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper( m_Mapper ); m_Actors->AddPart( outlineShadowActor ); m_Actors->AddPart( m_Actor ); } diff --git a/Core/Code/Testing/CMakeLists.txt b/Core/Code/Testing/CMakeLists.txt index 95d7cc96ef..7f5da021d8 100644 --- a/Core/Code/Testing/CMakeLists.txt +++ b/Core/Code/Testing/CMakeLists.txt @@ -1,242 +1,242 @@ # The core tests need relaxed compiler flags... # TODO fix core tests to compile without these additional no-error flags if(MSVC_VERSION) # disable deprecated warnings (they would lead to errors) mitkFunctionCheckCAndCXXCompilerFlags("/wd4996" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) else() mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-declarations" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() MITK_CREATE_MODULE_TESTS(US_MODULE) mitk_use_modules(TARGET ${TESTDRIVER} PACKAGES ITK|ITKThresholding) mitkAddCustomModuleTest(mitkVolumeCalculatorTest_Png2D-bw mitkVolumeCalculatorTest ${MITK_DATA_DIR}/Png2D-bw.png ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkEventMapperTest_Test1And2 mitkEventMapperTest ${MITK_DATA_DIR}/TestStateMachine1.xml ${MITK_DATA_DIR}/TestStateMachine2.xml ) mitkAddCustomModuleTest(mitkEventConfigTest_CreateObjectInDifferentWays mitkEventConfigTest ${MITK_SOURCE_DIR}/Core/Code/Testing/Resources/Interactions/StatemachineConfigTest.xml ) mitkAddCustomModuleTest(mitkNodeDependentPointSetInteractorTest mitkNodeDependentPointSetInteractorTest ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/BallBinary30x30x30.nrrd ) mitkAddCustomModuleTest(mitkDataStorageTest_US4DCyl mitkDataStorageTest ${MITK_DATA_DIR}/US4DCyl.nrrd ) mitkAddCustomModuleTest(mitkStateMachineFactoryTest_TestStateMachine1_2 mitkStateMachineFactoryTest ${MITK_DATA_DIR}/TestStateMachine1.xml ${MITK_DATA_DIR}/TestStateMachine2.xml ) mitkAddCustomModuleTest(mitkDicomSeriesReaderTest_CTImage mitkDicomSeriesReaderTest ${MITK_DATA_DIR}/TinyCTAbdomen ${MITK_DATA_DIR}/DICOMReader/Broken-Series ) mitkAddCustomModuleTest(mitkPointSetReaderTest mitkPointSetReaderTest ${MITK_DATA_DIR}/PointSetReaderTestData.mps ) mitkAddCustomModuleTest(mitkImageTest_4DImageData mitkImageTest ${MITK_DATA_DIR}/US4DCyl.nrrd ) mitkAddCustomModuleTest(mitkImageTest_2D+tImageData mitkImageTest ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkImageTest_3DImageData mitkImageTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkImageEqualTest mitkImageEqualTest) mitkAddCustomModuleTest(mitkImageTest_brainImage mitkImageTest ${MITK_DATA_DIR}/brain.mhd ) mitkAddCustomModuleTest(mitkImageTest_3DImageData mitkImageGeneratorTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkLevelWindowManagerTest mitkLevelWindowManagerTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkMultiComponentImageDataComparisonFilterTest mitkMultiComponentImageDataComparisonFilterTest ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg ) mitkAddCustomModuleTest(mitkImageToItkTest mitkImageToItkTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkImageSliceSelectorTest mitkImageSliceSelectorTest ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) if(MITK_ENABLE_RENDERING_TESTING) ### since the rendering test's do not run in ubuntu, yet, we build them only for other systems or if the user explicitly sets the variable MITK_ENABLE_RENDERING_TESTING mitkAddCustomModuleTest(mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2DTest ${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/rgbaImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3d640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3d640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dColorBlue640x480 mitkImageVtkMapper2DColorTest #test for color property (=blue) Pic3D sagittal slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dColorBlue640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dLevelWindow640x480 mitkImageVtkMapper2DLevelWindowTest #test for levelwindow property (=blood) #Pic3D sagittal slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dLevelWindowBlood640x480REF.png #corresponding reference #screenshot ) #mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dOpacity640x480 mitkImageVtkMapper2DOpacityTest #test for opacity (=0.5) Pic3D coronal slice # ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dOpacity640x480REF.png corresponding reference screenshot #) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dSwivel640x480 mitkImageVtkMapper2DSwivelTest #test for a randomly chosen Pic3D swivelled slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dSwivel640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_openMeAlone640x480 mitkPointSetVtkMapper2DTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAlone640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_Pic3DPointSetForPic3D640x480 mitkPointSetVtkMapper2DImageTest ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/RenderingTestData/PointSetForPic3D.mps #input point set and image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Pic3DPointSetForPic3D640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_openMeAloneGlyphType640x480 mitkPointSetVtkMapper2DGlyphTypeTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAloneGlyphType640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_openMeAloneTransformed640x480 mitkPointSetVtkMapper2DTransformedPointsTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAloneTransformedPoints640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkSurfaceDepthSortingTransparency_StanfordBunnySTL640x480 mitkSurfaceDepthSortingTest ${MITK_DATA_DIR}/RenderingTestData/Stanford_bunny.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Stanford_bunnySTLDepthSorting640x480REF.png ) if(NOT APPLE) mitkAddCustomModuleTest(mitkSurfaceDepthPeelingTransparency_StanfordBunnySTL640x480 mitkSurfaceDepthPeelingTest ${MITK_DATA_DIR}/RenderingTestData/Stanford_bunny.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Stanford_bunnySTLDepthPeeling640x480REF.png #corresponding reference screenshot ) endif() #Test reslice interpolation #note: nearest mode is already tested by swivel test mitkAddCustomModuleTest(ResliceInterpolationIsLinear mitkImageVtkMapper2DResliceInterpolationPropertyTest 1 #linear ${MITK_DATA_DIR}/Pic3D.nrrd -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dRefLinear.png #corresponding reference screenshot LINEAR ) mitkAddCustomModuleTest(ResliceInterpolationIsCubic mitkImageVtkMapper2DResliceInterpolationPropertyTest 3 #cubic ${MITK_DATA_DIR}/Pic3D.nrrd -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dRefCubic.png #corresponding reference screenshot CUBIC ) #End test reslice interpolation # Testing of the rendering of binary images #mitkAddCustomModuleTest(mitkImageVtkMapper2D_binaryTestImage640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice # ${MITK_DATA_DIR}/RenderingTestData/binaryImage.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/binaryImage640x480REF.png #corresponding reference screenshot #) #mitkAddCustomModuleTest(mitkImageVtkMapper2D_binaryTestImageWithRef640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice # ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/RenderingTestData/binaryImage.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/binaryImageWithRef640x480REF.png #corresponding reference screenshot #) # End of binary image tests mitkAddCustomModuleTest(mitkSurfaceVtkMapper3DTest_TextureProperty mitkSurfaceVtkMapper3DTest ${MITK_DATA_DIR}/ToF-Data/Kinect_LiverPhantom.vtp ${MITK_DATA_DIR}/ToF-Data/Kinect_LiverPhantom_RGBImage.nrrd -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/texturedLiver640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2DTransferFunctionTest_Png2D-bw mitkImageVtkMapper2DTransferFunctionTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-TransferFunctionRGBImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2DOpacityTransferFunctionTest_Png2D-bw mitkImageVtkMapper2DOpacityTransferFunctionTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-OpacityTransferFunctionRGBImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkSurfaceGLMapper2DColorTest_RedBall mitkSurfaceGLMapper2DColorTest ${MITK_DATA_DIR}/ball.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/ballColorRed640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkSurfaceGLMapper2DColorTest_DasArmeSchwein mitkSurfaceGLMapper2DColorTest ${MITK_DATA_DIR}/binary.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/binaryColorRed640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkSurfaceGLMapper2DOpacityTest_BallOpacity mitkSurfaceGLMapper2DOpacityTest #opacity = 50% (0.5) ${MITK_DATA_DIR}/ball.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/ballOpacity640x480REF.png #corresponding reference screenshot ) ############################## DISABLED TESTS #Removed due to high rendering error. #mitkAddCustomModuleTest(mitkSurfaceVtkMapper3DTexturedSphereTest_Football mitkSurfaceVtkMapper3DTexturedSphereTest # ${MITK_DATA_DIR}/RenderingTestData/texture.jpg #input texture # -V # ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/texturedSphere640x480REF.png corresponding reference screenshot #) -#mitkAddCustomModuleTest(mitkImageVtkMapper2DLookupTableTest_Png2D-bw mitkImageVtkMapper2DLookupTableTest -# ${MITK_DATA_DIR}/Png2D-bw.png -# -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-LookupTableRGBImage640x480REF.png #corresponding reference screenshot -#) +mitkAddCustomModuleTest(mitkImageVtkMapper2DLookupTableTest_Png2D-bw mitkImageVtkMapper2DLookupTableTest + ${MITK_DATA_DIR}/Png2D-bw.png + -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-LookupTableRGBImage640x480REF.png #corresponding reference screenshot +) #mitkAddCustomModuleTest(mitkImageTest_color2DImage mitkImageTest # ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg #) #mitkAddCustomModuleTest(mitkNodeDependentPointSetInteractorTest mitkNodeDependentPointSetInteractorTest # ${MITK_DATA_DIR}/Pic3D.pic.gz ${MITK_DATA_DIR}/BallBinary30x30x30.pic.gz #) SET_PROPERTY(TEST mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2D_pic3d640x480 mitkImageVtkMapper2D_pic3dColorBlue640x480 mitkImageVtkMapper2D_pic3dLevelWindow640x480 mitkImageVtkMapper2D_pic3dSwivel640x480 mitkImageVtkMapper2DTransferFunctionTest_Png2D-bw # mitkImageVtkMapper2D_pic3dOpacity640x480 mitkSurfaceGLMapper2DOpacityTest_BallOpacity mitkSurfaceGLMapper2DColorTest_DasArmeSchwein mitkSurfaceGLMapper2DColorTest_RedBall mitkSurfaceVtkMapper3DTest_TextureProperty mitkPointSetVtkMapper2D_Pic3DPointSetForPic3D640x480 mitkPointSetVtkMapper2D_openMeAlone640x480 mitkPointSetVtkMapper2D_openMeAloneGlyphType640x480 mitkPointSetVtkMapper2D_openMeAloneTransformed640x480 #mitkSurfaceVtkMapper3DTexturedSphereTest_Football PROPERTY RUN_SERIAL TRUE) endif() add_test(mitkPointSetLocaleTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkPointSetLocaleTest ${MITK_DATA_DIR}/pointSet.mps) set_property(TEST mitkPointSetLocaleTest PROPERTY LABELS MITK-Core) add_test(mitkImageWriterTest_nrrdImage ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkImageWriterTest ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg) add_test(mitkImageWriterTest_2DPNGImage ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkImageWriterTest ${MITK_DATA_DIR}/Png2D-bw.png) add_test(mitkImageWriterTest_rgbPNGImage ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkImageWriterTest ${MITK_DATA_DIR}/RenderingTestData/rgbImage.png) add_test(mitkImageWriterTest_rgbaPNGImage ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkImageWriterTest ${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png) set_property(TEST mitkImageWriterTest_nrrdImage PROPERTY LABELS MITK-Core) set_property(TEST mitkImageWriterTest_2DPNGImage PROPERTY LABELS MITK-Core) set_property(TEST mitkImageWriterTest_rgbPNGImage PROPERTY LABELS MITK-Core) set_property(TEST mitkImageWriterTest_rgbaPNGImage PROPERTY LABELS MITK-Core) add_subdirectory(DCMTesting) diff --git a/Core/Code/Testing/DCMTesting/Testing/files.cmake b/Core/Code/Testing/DCMTesting/Testing/files.cmake index 1b775fa79a..2d9380c11a 100644 --- a/Core/Code/Testing/DCMTesting/Testing/files.cmake +++ b/Core/Code/Testing/DCMTesting/Testing/files.cmake @@ -1,11 +1,6 @@ # tests with no extra command line parameter set(MODULE_CUSTOM_TESTS mitkDCMTestingSanityTest.cpp mitkDCMPreloadedVolumeTest.cpp ) - -# this shouldn't be necessary if this variable -# would actually be a parameter of the MITK_CREATE_MODULE_TESTS -# macro. See bug #10592 -set(TEST_CPP_FILES "") diff --git a/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.cpp b/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.cpp index e430bc7a04..f4417097c4 100644 --- a/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.cpp +++ b/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.cpp @@ -1,445 +1,444 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include #include "mitkImage.h" #include "mitkTestDCMLoading.h" - #include mitk::TestDCMLoading::TestDCMLoading() :m_PreviousCLocale(NULL) { } void mitk::TestDCMLoading::SetDefaultLocale() { // remember old locale only once if (m_PreviousCLocale == NULL) { m_PreviousCLocale = setlocale(LC_NUMERIC, NULL); // set to "C" setlocale(LC_NUMERIC, "C"); m_PreviousCppLocale = std::cin.getloc(); std::locale l( "C" ); std::cin.imbue(l); std::cout.imbue(l); } } void mitk::TestDCMLoading::ResetUserLocale() { if (m_PreviousCLocale) { setlocale(LC_NUMERIC, m_PreviousCLocale); std::cin.imbue(m_PreviousCppLocale); std::cout.imbue(m_PreviousCppLocale); m_PreviousCLocale = NULL; } } mitk::TestDCMLoading::ImageList mitk::TestDCMLoading::LoadFiles( const StringContainer& files, itk::SmartPointer preLoadedVolume ) { for (StringContainer::const_iterator iter = files.begin(); iter != files.end(); ++iter) { MITK_DEBUG << "File " << *iter; } ImageList result; DicomSeriesReader::FileNamesGrouping seriesInFiles = DicomSeriesReader::GetSeries( files, true ); // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) for (DicomSeriesReader::FileNamesGrouping::const_iterator seriesIter = seriesInFiles.begin(); seriesIter != seriesInFiles.end(); ++seriesIter) { StringContainer files = seriesIter->second.GetFilenames(); DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( files, true, true, true, 0, preLoadedVolume ); // true, true, true ist just a copy of the default values if (node.IsNotNull()) { Image::Pointer image = dynamic_cast( node->GetData() ); result.push_back( image ); } else { } } return result; } std::string mitk::TestDCMLoading::ComponentTypeToString(int type) { if (type == itk::ImageIOBase::UCHAR) return "UCHAR"; else if (type == itk::ImageIOBase::CHAR) return "CHAR"; else if (type == itk::ImageIOBase::USHORT) return "USHORT"; else if (type == itk::ImageIOBase::SHORT) return "SHORT"; else if (type == itk::ImageIOBase::UINT) return "UINT"; else if (type == itk::ImageIOBase::INT) return "INT"; else if (type == itk::ImageIOBase::ULONG) return "ULONG"; else if (type == itk::ImageIOBase::LONG) return "LONG"; else if (type == itk::ImageIOBase::FLOAT) return "FLOAT"; else if (type == itk::ImageIOBase::DOUBLE) return "DOUBLE"; else return "UNKNOWN"; } // add a line to stringstream result (see DumpImageInformation #define DumpLine(field, data) DumpILine(0, field, data) // add an indented(!) line to stringstream result (see DumpImageInformation #define DumpILine(indent, field, data) \ { \ std::string DumpLine_INDENT; DumpLine_INDENT.resize(indent, ' ' ); \ result << DumpLine_INDENT << field << ": " << data << "\n"; \ } std::string mitk::TestDCMLoading::DumpImageInformation( const Image* image ) { std::stringstream result; if (image == NULL) return result.str(); SetDefaultLocale(); // basic image data DumpLine( "Pixeltype", ComponentTypeToString(image->GetPixelType().GetComponentType()) ); DumpLine( "BitsPerPixel", image->GetPixelType().GetBpe() ); DumpLine( "Dimension", image->GetDimension() ); result << "Dimensions: "; for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) result << image->GetDimension(dim) << " "; result << "\n"; // geometry data result << "Geometry: \n"; BaseGeometry* geometry = image->GetGeometry(); if (geometry) { AffineTransform3D* transform = geometry->GetIndexToWorldTransform(); if (transform) { result << " " << "Matrix: "; const AffineTransform3D::MatrixType& matrix = transform->GetMatrix(); for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) result << matrix[i][j] << " "; result << "\n"; result << " " << "Offset: "; const AffineTransform3D::OutputVectorType& offset = transform->GetOffset(); for (unsigned int i = 0; i < 3; ++i) result << offset[i] << " "; result << "\n"; result << " " << "Center: "; const AffineTransform3D::InputPointType& center = transform->GetCenter(); for (unsigned int i = 0; i < 3; ++i) result << center[i] << " "; result << "\n"; result << " " << "Translation: "; const AffineTransform3D::OutputVectorType& translation = transform->GetTranslation(); for (unsigned int i = 0; i < 3; ++i) result << translation[i] << " "; result << "\n"; result << " " << "Scale: "; const double* scale = transform->GetScale(); for (unsigned int i = 0; i < 3; ++i) result << scale[i] << " "; result << "\n"; result << " " << "Origin: "; const Point3D& origin = geometry->GetOrigin(); for (unsigned int i = 0; i < 3; ++i) result << origin[i] << " "; result << "\n"; result << " " << "Spacing: "; const Vector3D& spacing = geometry->GetSpacing(); for (unsigned int i = 0; i < 3; ++i) result << spacing[i] << " "; result << "\n"; result << " " << "TimeBounds: "; const TimeBounds timeBounds = image->GetTimeGeometry()->GetTimeBounds(); for (unsigned int i = 0; i < 2; ++i) result << timeBounds[i] << " "; result << "\n"; } } ResetUserLocale(); return result.str(); } std::string mitk::TestDCMLoading::trim(const std::string& pString, const std::string& pWhitespace) { const size_t beginStr = pString.find_first_not_of(pWhitespace); if (beginStr == std::string::npos) { // no content return ""; } const size_t endStr = pString.find_last_not_of(pWhitespace); const size_t range = endStr - beginStr + 1; return pString.substr(beginStr, range); } std::string mitk::TestDCMLoading::reduce(const std::string& pString, const std::string& pFill, const std::string& pWhitespace) { // trim first std::string result(trim(pString, pWhitespace)); // replace sub ranges size_t beginSpace = result.find_first_of(pWhitespace); while (beginSpace != std::string::npos) { const size_t endSpace = result.find_first_not_of(pWhitespace, beginSpace); const size_t range = endSpace - beginSpace; result.replace(beginSpace, range, pFill); const size_t newStart = beginSpace + pFill.length(); beginSpace = result.find_first_of(pWhitespace, newStart); } return result; } bool mitk::TestDCMLoading::CompareSpacedValueFields( const std::string& reference, const std::string& test, double /*eps*/ ) { bool result(true); // tokenize string, compare each token, if possible by float comparison std::stringstream referenceStream(reduce(reference)); std::stringstream testStream(reduce(test)); std::string refToken; std::string testToken; while ( std::getline( referenceStream, refToken, ' ' ) && std::getline ( testStream, testToken, ' ' ) ) { float refNumber; float testNumber; if ( this->StringToNumber(refToken, refNumber) ) { if ( this->StringToNumber(testToken, testNumber) ) { // print-out compared tokens if DEBUG output allowed MITK_DEBUG << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; bool old_result = result; result &= ( fabs(refNumber - testNumber) < 0.0001 /*mitk::eps*/ ); // log the token/number which causes the test to fail if( old_result != result) { MITK_ERROR << std::setprecision(16) << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; MITK_ERROR << "[FALSE] - difference: " << std::setprecision(16) << fabs(refNumber - testNumber) << " EPS: " << 0.0001;// mitk::eps; } } else { MITK_ERROR << refNumber << " cannot be compared to '" << testToken << "'"; } } else { MITK_DEBUG << "Token '" << refToken << "'" << " handled as string"; result &= refToken == testToken; } } if ( std::getline( referenceStream, refToken, ' ' ) ) { MITK_ERROR << "Reference string still had values when test string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } else if ( std::getline( testStream, testToken, ' ' ) ) { MITK_ERROR << "Test string still had values when reference string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } return result; } bool mitk::TestDCMLoading::CompareImageInformationDumps( const std::string& referenceDump, const std::string& testDump ) { KeyValueMap reference = ParseDump(referenceDump); KeyValueMap test = ParseDump(testDump); bool testResult(true); // verify all expected values for (KeyValueMap::const_iterator refIter = reference.begin(); refIter != reference.end(); ++refIter) { const std::string& refKey = refIter->first; const std::string& refValue = refIter->second; if ( test.find(refKey) != test.end() ) { const std::string& testValue = test[refKey]; bool thisTestResult = CompareSpacedValueFields( refValue, testValue ); testResult &= thisTestResult; MITK_DEBUG << refKey << ": '" << refValue << "' == '" << testValue << "' ? " << (thisTestResult?"YES":"NO"); } else { MITK_ERROR << "Reference dump contains a key'" << refKey << "' (value '" << refValue << "')." ; MITK_ERROR << "This key is expected to be generated for tests (but was not). Most probably you need to update your test data."; return false; } } // now check test dump does not contain any additional keys for (KeyValueMap::const_iterator testIter = test.begin(); testIter != test.end(); ++testIter) { const std::string& key = testIter->first; const std::string& value = testIter->second; if ( reference.find(key) == reference.end() ) { MITK_ERROR << "Test dump contains an unexpected key'" << key << "' (value '" << value << "')." ; MITK_ERROR << "This key is not expected. Most probably you need to update your test data."; return false; } } return testResult; } mitk::TestDCMLoading::KeyValueMap mitk::TestDCMLoading::ParseDump( const std::string& dump ) { KeyValueMap parsedResult; std::string shredder(dump); std::stack surroundingKeys; std::stack expectedIndents; expectedIndents.push(0); while (true) { std::string::size_type newLinePos = shredder.find( '\n' ); if (newLinePos == std::string::npos || newLinePos == 0) break; std::string line = shredder.substr( 0, newLinePos ); shredder = shredder.erase( 0, newLinePos+1 ); std::string::size_type keyPosition = line.find_first_not_of( ' ' ); std::string::size_type colonPosition = line.find( ':' ); std::string key = line.substr(keyPosition, colonPosition - keyPosition); std::string::size_type firstSpacePosition = key.find_first_of(" "); if (firstSpacePosition != std::string::npos) { key.erase(firstSpacePosition); } if ( keyPosition > expectedIndents.top() ) { // more indent than before expectedIndents.push(keyPosition); } else if (keyPosition == expectedIndents.top() ) { if (!surroundingKeys.empty()) { surroundingKeys.pop(); // last of same length } } else { // less indent than before do expectedIndents.pop(); while (expectedIndents.top() != keyPosition); // unwind until current indent is found } if (!surroundingKeys.empty()) { key = surroundingKeys.top() + "." + key; // construct current key name } surroundingKeys.push(key); // this is the new embracing key std::string value = line.substr(colonPosition+1); MITK_DEBUG << " Key: '" << key << "' value '" << value << "'" ; parsedResult[key] = value; // store parsing result } return parsedResult; } diff --git a/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.h b/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.h index b0a1552d3a..5ac73a1071 100644 --- a/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.h +++ b/Core/Code/Testing/DCMTesting/mitkTestDCMLoading.h @@ -1,108 +1,109 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkTestDCMLoading_h #define mitkTestDCMLoading_h #include "mitkDicomSeriesReader.h" #include "MitkDCMTestingExports.h" namespace mitk { + class Image; class MitkDCMTesting_EXPORT TestDCMLoading { public: typedef DicomSeriesReader::StringContainer StringContainer; typedef std::list< DataNode::Pointer > NodeList; typedef std::list< itk::SmartPointer > ImageList; TestDCMLoading(); ImageList LoadFiles( const StringContainer& files, itk::SmartPointer preLoadedVolume = NULL ); /** \brief Dump relevant image information for later comparison. \sa CompareImageInformationDumps */ std::string DumpImageInformation( const Image* image ); /** \brief Compare two image information dumps. \return true, if dumps are sufficiently equal (see parameters) \sa DumpImageInformation */ bool CompareImageInformationDumps( const std::string& reference, const std::string& test ); private: typedef std::map KeyValueMap; void SetDefaultLocale(); void ResetUserLocale(); std::string ComponentTypeToString( int type ); KeyValueMap ParseDump( const std::string& dump ); bool CompareSpacedValueFields( const std::string& reference, const std::string& test, double eps = mitk::eps ); /** Compress whitespace in string \param pString input string \param pFill replacement whitespace (only whitespace in string after reduction) \param pWhitespace characters handled as whitespace */ std::string reduce(const std::string& pString, const std::string& pFill = " ", const std::string& pWhitespace = " \t"); /** Remove leading and trailing whitespace \param pString input string \param pWhitespace characters handled as whitespace */ std::string trim(const std::string& pString, const std::string& pWhitespace = " \t"); template bool StringToNumber(const std::string& s, T& value) { std::stringstream stream(s); stream >> value; return (!stream.fail()) && (fabs(value) < std::numeric_limits::max()); } const char* m_PreviousCLocale; std::locale m_PreviousCppLocale; }; } #endif diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index 939ba93d9b..9f9cdaa198 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,199 +1,190 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkDataNodeTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFileReaderRegistryTest.cpp #mitkFileWriterRegistryTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageCastTest.cpp mitkImageEqualTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetInteractorTest.cpp mitkPointSetPointOperationsTest.cpp mitkProgressBarTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkProportionalTimeGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkShaderRepositoryTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkPointTypeConversionTest.cpp mitkVectorTypeConversionTest.cpp mitkMatrixTypeConversionTest.cpp mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp ) if(MITK_ENABLE_RENDERING_TESTING) #since mitkInteractionTestHelper is currently creating a vtkRenderWindow set(MODULE_TESTS ${MODULE_TESTS} mitkPointSetDataInteractorTest.cpp ) endif() # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces ) # list of images for which the tests are run set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACES binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp + mitkDataNodeTest.cpp mitkEventMapperTest.cpp mitkEventConfigTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageWriterTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DOpacityTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest mitkSurfaceVtkMapper3DTexturedSphereTest.cpp mitkSurfaceGLMapper2DColorTest.cpp mitkSurfaceGLMapper2DOpacityTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkSurfaceDepthPeelingTest.cpp mitkSurfaceDepthSortingTest.cpp ) -set(MODULE_RESOURCE_FILES +set(RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) - -# Embed the resources -set(testdriver_resources ) -usFunctionEmbedResources(testdriver_resources - EXECUTABLE_NAME ${MODULE_NAME}TestDriver - ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Resources - FILES ${MODULE_RESOURCE_FILES} - ) - -set(TEST_CPP_FILES ${testdriver_init_file} ${testdriver_resources}) diff --git a/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp b/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp index 5f9fdf4f36..e201a48026 100644 --- a/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp +++ b/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp @@ -1,163 +1,162 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkDicomSeriesReader.h" #include "mitkProperties.h" #include "mitkImage.h" - static std::map > GetTagInformationFromFile(mitk::DicomSeriesReader::StringContainer files) { gdcm::Scanner scanner; std::map > tagInformations; const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag( tagSliceLocation ); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag( tagInstanceNumber ); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag( tagSOPInstanceNumber ); //unsigned int slice(0); scanner.Scan(files); // return const_cast(scanner.GetMappings()); gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); for(std::vector::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter) { std::map tags; tags.insert(std::pair (tagSliceLocation, tagValueMappings[fIter->c_str()][tagSliceLocation])); tags.insert(std::pair (tagInstanceNumber, tagValueMappings[fIter->c_str()][tagInstanceNumber])); tags.insert(std::pair (tagSOPInstanceNumber, tagValueMappings[fIter->c_str()][tagSOPInstanceNumber])); tagInformations.insert(std::pair > (fIter->c_str(), tags)); } return tagInformations; } int mitkDicomSeriesReaderTest(int argc, char* argv[]) { // always start with this! MITK_TEST_BEGIN("DicomSeriesReader") if(argc < 1) { MITK_ERROR << "No directory given!"; return -1; } char* dir; dir = argv[1]; //check if DICOMTags have been set as property for mitk::Image mitk::DicomSeriesReader::FileNamesGrouping seriesInFiles = mitk::DicomSeriesReader::GetSeries( dir, true ); std::list images; std::map fileMap; // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) for (mitk::DicomSeriesReader::FileNamesGrouping::const_iterator seriesIter = seriesInFiles.begin(); seriesIter != seriesInFiles.end(); ++seriesIter) { mitk::DicomSeriesReader::StringContainer files = seriesIter->second.GetFilenames(); mitk::DataNode::Pointer node = mitk::DicomSeriesReader::LoadDicomSeries( files ); MITK_TEST_CONDITION_REQUIRED(node.IsNotNull(),"Testing node") if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); images.push_back( image ); fileMap.insert( std::pair(image,files)); } } //Test if DICOM tags have been added correctly to the mitk::image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number for ( std::list::const_iterator imageIter = images.begin(); imageIter != images.end(); ++imageIter ) { const mitk::Image::Pointer image = *imageIter; //Get tag information for all dicom files of this image std::map > tagInformations = GetTagInformationFromFile((*fileMap.find(image)).second); mitk::StringLookupTableProperty* sliceLocation = dynamic_cast(image->GetProperty("dicom.image.0020.1041").GetPointer()); mitk::StringLookupTableProperty* instanceNumber = dynamic_cast(image->GetProperty("dicom.image.0020.0013").GetPointer()); mitk::StringLookupTableProperty* SOPInstnaceNumber = dynamic_cast(image->GetProperty("dicom.image.0008.0018").GetPointer()); mitk::StringLookupTableProperty* files = dynamic_cast(image->GetProperty("files").GetPointer()); MITK_TEST_CONDITION(sliceLocation != NULL, "Test if tag for slice location has been set to mitk image"); if(sliceLocation != NULL) { for(int i = 0; i < (int)sliceLocation->GetValue().GetLookupTable().size(); i++) { if(i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << sliceLocation->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSliceLocation] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(sliceLocation->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSliceLocation], "Test if value for slice location is correct"); } } } MITK_TEST_CONDITION(instanceNumber != NULL, "Test if tag for image instance number has been set to mitk image"); if(instanceNumber != NULL) { for(int i = 0; i < (int)instanceNumber->GetValue().GetLookupTable().size(); i++) { if(i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << instanceNumber->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagInstanceNumber] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(instanceNumber->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagInstanceNumber], "Test if value for instance number is correct"); } } } MITK_TEST_CONDITION(SOPInstnaceNumber != NULL, "Test if tag for SOP instance number has been set to mitk image"); if(SOPInstnaceNumber != NULL) { for(int i = 0; i < (int)SOPInstnaceNumber->GetValue().GetLookupTable().size(); i++) { if(i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << instanceNumber->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSOPInstanceNumber] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(SOPInstnaceNumber->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSOPInstanceNumber], "Test if value for SOP instance number is correct"); } } } } MITK_TEST_END() } diff --git a/Core/Code/Testing/mitkPointSetReaderTest.cpp b/Core/Code/Testing/mitkPointSetReaderTest.cpp index 6967e3f01d..5d8ccfdda0 100644 --- a/Core/Code/Testing/mitkPointSetReaderTest.cpp +++ b/Core/Code/Testing/mitkPointSetReaderTest.cpp @@ -1,69 +1,69 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSet.h" #include "mitkTestingMacros.h" #include "mitkFileReaderRegistry.h" #include "mitkMimeType.h" /** * Test for the class "mitkPointSetReader". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test data set for the tests (see CMakeLists.txt). */ int mitkPointSetReaderTest(int argc , char* argv[]) { // always start with this! MITK_TEST_BEGIN("PointSetReader") MITK_TEST_CONDITION_REQUIRED(argc == 2,"Testing invocation") mitk::FileReaderRegistry readerRegistry; // Get PointSet reader(s) - std::vector readers = readerRegistry.GetReaders(mitk::FileReaderRegistry::GetMimeTypeForExtension("mps")); + std::vector readers = readerRegistry.GetReaders(mitk::FileReaderRegistry::GetMimeTypeForFile("mps")); MITK_TEST_CONDITION_REQUIRED(!readers.empty(), "Testing for registered readers") for (std::vector::const_iterator iter = readers.begin(), end = readers.end(); iter != end; ++iter) { std::string testName = "test1"; mitk::IFileReader* reader = *iter; reader->SetInput(testName); // testing file reading with invalid data MITK_TEST_CONDITION_REQUIRED(reader->GetConfidenceLevel() == mitk::IFileReader::Unsupported, "Testing confidence level with invalid input file name!"); CPPUNIT_ASSERT_THROW(reader->Read(), mitk::Exception); // testing file reading with valid data std::string filePath = argv[1]; reader->SetInput(filePath); MITK_TEST_CONDITION_REQUIRED( reader->GetConfidenceLevel() == mitk::IFileReader::Supported, "Testing confidence level with valid input file name!"); std::vector data = reader->Read(); MITK_TEST_CONDITION_REQUIRED( !data.empty(), "Testing non-empty data with valid input file name!"); // evaluate if the read point set is correct mitk::PointSet::Pointer resultPS = dynamic_cast(data.front().GetPointer()); MITK_TEST_CONDITION_REQUIRED( resultPS.IsNotNull(), "Testing correct BaseData type"); MITK_TEST_CONDITION_REQUIRED( resultPS->GetTimeSteps() == 14, "Testing output time step generation!"); // CAVE: Only valid with the specified test data! MITK_TEST_CONDITION_REQUIRED( resultPS->GetPointSet(resultPS->GetTimeSteps()-1)->GetNumberOfPoints() == 0, "Testing output time step generation with empty time step!"); // CAVE: Only valid with the specified test data! } // always end with this! MITK_TEST_END() } diff --git a/Core/Code/TestingHelper/mitkTestingMacros.h b/Core/Code/TestingHelper/mitkTestingMacros.h index 02edc5fe47..dcf2de6393 100644 --- a/Core/Code/TestingHelper/mitkTestingMacros.h +++ b/Core/Code/TestingHelper/mitkTestingMacros.h @@ -1,386 +1,385 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkTestingMacros_h #define mitkTestingMacros_h #include #include #include #include #include #include #include #include #include namespace mitk { /** @brief Indicate a failed test. */ class TestFailedException : public std::exception { public: TestFailedException() {} }; } /** * @brief Output some text without generating a terminating newline. Include * * @ingroup MITKTestingAPI */ #define MITK_TEST_OUTPUT_NO_ENDL(x) \ std::cout x ; /** * @brief Output some text. * * @ingroup MITKTestingAPI */ #define MITK_TEST_OUTPUT(x) \ MITK_TEST_OUTPUT_NO_ENDL(x << "\n") /** * @brief Do some general test preparations. Must be called first in the * main test function. * * @deprecatedSince{2013_09} Use MITK_TEST_SUITE_REGISTRATION instead. * @ingroup MITKTestingAPI */ #define MITK_TEST_BEGIN(testName) \ std::string mitkTestName(#testName); \ mitk::TestManager::GetInstance()->Initialize(); \ try { /** * @brief Fail and finish test with message MSG * * @deprecatedSince{2013_09} Use CPPUNIT_FAIL instead * @ingroup MITKTestingAPI */ #define MITK_TEST_FAILED_MSG(MSG) \ MITK_TEST_OUTPUT(MSG) \ throw mitk::TestFailedException(); /** * @brief Must be called last in the main test function. * * @deprecatedSince{2013_09} Use MITK_TEST_SUITE_REGISTRATION instead. * @ingroup MITKTestingAPI */ #define MITK_TEST_END() \ } catch (const mitk::TestFailedException&) { \ MITK_TEST_OUTPUT(<< "Further test execution skipped.") \ mitk::TestManager::GetInstance()->TestFailed(); \ } catch (const std::exception& ex) { \ MITK_TEST_OUTPUT(<< "std::exception occured " << ex.what()) \ mitk::TestManager::GetInstance()->TestFailed(); \ } \ if (mitk::TestManager::GetInstance()->NumberOfFailedTests() > 0) { \ MITK_TEST_OUTPUT(<< mitkTestName << ": [DONE FAILED] , subtests passed: " << \ mitk::TestManager::GetInstance()->NumberOfPassedTests() << " failed: " << \ mitk::TestManager::GetInstance()->NumberOfFailedTests() ) \ return EXIT_FAILURE; \ } else { \ MITK_TEST_OUTPUT(<< mitkTestName << ": " \ << mitk::TestManager::GetInstance()->NumberOfPassedTests() \ << " tests [DONE PASSED]") \ return EXIT_SUCCESS; \ } \ /** * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT or CPPUNIT_ASSERT_MESSAGE instead. */ #define MITK_TEST_CONDITION(COND,MSG) \ MITK_TEST_OUTPUT_NO_ENDL(<< MSG) \ if ( ! (COND) ) { \ mitk::TestManager::GetInstance()->TestFailed(); \ MITK_TEST_OUTPUT(<< " [FAILED]\n" << "In " << __FILE__ \ << ", line " << __LINE__ \ << ": " #COND " : [FAILED]") \ } else { \ MITK_TEST_OUTPUT(<< " [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT or CPPUNIT_ASSERT_MESSAGE instead. */ #define MITK_TEST_CONDITION_REQUIRED(COND,MSG) \ MITK_TEST_OUTPUT_NO_ENDL(<< MSG) \ if ( ! (COND) ) { \ MITK_TEST_FAILED_MSG(<< " [FAILED]\n" << " +--> in " << __FILE__ \ << ", line " << __LINE__ \ << ", expression is false: \"" #COND "\"") \ } else { \ MITK_TEST_OUTPUT(<< " [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * \brief Begin block which should be checked for exceptions * * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT_THROW instead. * @ingroup MITKTestingAPI * * This macro, together with MITK_TEST_FOR_EXCEPTION_END, can be used * to test whether a code block throws an expected exception. The test FAILS if the * exception is NOT thrown. A simple example: * MITK_TEST_FOR_EXCEPTION_BEGIN(itk::ImageFileReaderException) typedef itk::ImageFileReader< itk::Image > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName("/tmp/not-existing"); reader->Update(); MITK_TEST_FOR_EXCEPTION_END(itk::ImageFileReaderException) * */ #define MITK_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ try { /** * @deprecatedSince{2013_09} */ #define MITK_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) \ mitk::TestManager::GetInstance()->TestFailed(); \ MITK_TEST_OUTPUT( << "Expected an '" << #EXCEPTIONCLASS << "' exception. [FAILED]") \ } \ catch (EXCEPTIONCLASS) { \ MITK_TEST_OUTPUT( << "Caught an expected '" << #EXCEPTIONCLASS \ << "' exception. [PASSED]") \ mitk::TestManager::GetInstance()->TestPassed(); \ } /** * @brief Simplified version of MITK_TEST_FOR_EXCEPTION_BEGIN / END for * a single statement * * @deprecatedSince{2013_09} Use CPPUNIT_ASSERT_THROW instead. * @ingroup MITKTestingAPI */ #define MITK_TEST_FOR_EXCEPTION(EXCEPTIONCLASS, STATEMENT) \ MITK_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ STATEMENT ; \ MITK_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) /** * @brief Testing macro to test if two objects are equal. * * @ingroup MITKTestingAPI * * This macro uses mitk::eps and the corresponding mitk::Equal methods for all * comparisons and will give verbose output on the dashboard/console. * Feel free to implement mitk::Equal for your own datatype or purpose. * * @deprecatedSince{2013_09} Use MITK_ASSERT_EQUAL instead. * * @param OBJ1 First object. * @param OBJ2 Second object. * @param MSG Message to appear with the test. */ #define MITK_TEST_EQUAL(OBJ1,OBJ2,MSG) \ MITK_TEST_CONDITION_REQUIRED( mitk::Equal(OBJ1, OBJ2, mitk::eps, true)==true, MSG) /** * @brief Testing macro to test if two objects are equal. * * @ingroup MITKTestingAPI * * This macro uses mitk::eps and the corresponding mitk::Equal methods for all * comparisons and will give verbose output on the dashboard/console. * Feel free to implement mitk::Equal for your own datatype or purpose. * * @param EXPECTED First object. * @param ACTUAL Second object. * @param MSG Message to appear with the test. * @throw Throws mitkException if a NULL pointer is given as input. */ #define MITK_ASSERT_EQUAL(EXPECTED, ACTUAL, MSG) \ if(((EXPECTED).IsNull()) || ((ACTUAL).IsNull())) { \ mitkThrow() << "mitk::Equal does not work with NULL pointer input."; \ } \ CPPUNIT_ASSERT_MESSAGE(MSG, mitk::Equal(*(EXPECTED), *(ACTUAL), mitk::eps, true)) /** * @brief Testing macro to test if two objects are not equal. * * @ingroup MITKTestingAPI * * This macro uses mitk::eps and the corresponding mitk::Equal methods for all * comparisons and will give verbose output on the dashboard/console. * * @deprecatedSince{2013_09} Use MITK_ASSERT_NOT_EQUAL instead. * * @param OBJ1 First object. * @param OBJ2 Second object. * @param MSG Message to appear with the test. * * \sa MITK_TEST_EQUAL */ #define MITK_TEST_NOT_EQUAL(OBJ1,OBJ2,MSG) \ CPPUNIT_ASSERT_MESSAGE(MSG, !mitk::Equal(*(OBJ1), *(OBJ2), mitk::eps, true)) /** * @brief Testing macro to test if two objects are not equal. * * @ingroup MITKTestingAPI * * This macro uses mitk::eps and the corresponding mitk::Equal methods for all * comparisons and will give verbose output on the dashboard/console. * * @param OBJ1 First object. * @param OBJ2 Second object. * @param MSG Message to appear with the test. * @throw Throws mitkException if a NULL pointer is given as input. * * \sa MITK_ASSERT_EQUAL */ #define MITK_ASSERT_NOT_EQUAL(OBJ1, OBJ2, MSG) \ if(((OBJ1).IsNull()) || ((OBJ2).IsNull())) { \ mitkThrow() << "mitk::Equal does not work with NULL pointer input."; \ } \ CPPUNIT_ASSERT_MESSAGE(MSG, !mitk::Equal(*(OBJ1), *(OBJ2), mitk::eps, true)) /** * @brief Registers the given test suite. * * @ingroup MITKTestingAPI * * @param TESTSUITE_NAME The name of the test suite class, without "TestSuite" * at the end. */ #define MITK_TEST_SUITE_REGISTRATION(TESTSUITE_NAME) \ int TESTSUITE_NAME ## Test(int /*argc*/, char* /*argv*/[]) \ { \ CppUnit::TextUi::TestRunner runner; \ runner.addTest(TESTSUITE_NAME ## TestSuite::suite()); \ return runner.run() ? 0 : 1; \ } /** * @brief Adds a test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases. * The macro internally just calls the CPPUNIT_TEST macro. * * @param TESTMETHOD The name of the member funtion test. */ #define MITK_TEST(TESTMETHOD) CPPUNIT_TEST(TESTMETHOD) /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need custom parameters. * * @param TESTMETHOD The name of the member function test. * @param ARGS A std::vector object containing test parameter. * * @note Use the macro MITK_PARAMETERIZED_TEST only if you know what * you are doing. If you are not sure, use MITK_TEST instead. */ #define MITK_PARAMETERIZED_TEST(TESTMETHOD, ARGS) \ { \ std::string testName = #TESTMETHOD; \ for (std::size_t i = 0; i < ARGS.size(); ++i) \ { \ testName += "_" + ARGS[i]; \ } \ CPPUNIT_TEST_SUITE_ADD_TEST( \ ( new mitk::TestCaller( \ context.getTestNameFor(testName), \ &TestFixtureType::TESTMETHOD, \ context.makeFixture(), args ) ) ); \ } /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need parameters from the command line. * * @warning Use the macro MITK_PARAMETERIZED_CMD_LINE_TEST only * if you know what you are doing. If you are not sure, use * MITK_TEST instead. MITK_PARAMETERIZED_CMD_LINE_TEST is meant * for migrating from ctest to CppUnit. If you implement new * tests, the MITK_TEST macro will be sufficient. * * @param TESTMETHOD The name of the member function test. */ #define MITK_PARAMETERIZED_CMD_LINE_TEST(TESTMETHOD) \ CPPUNIT_TEST_SUITE_ADD_TEST( \ ( new mitk::TestCaller( \ context.getTestNameFor( #TESTMETHOD), \ &TestFixtureType::TESTMETHOD, \ context.makeFixture() ) ) ); /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need one custom parameter. * * @param TESTMETHOD The name of the member function test. * @param arg1 A custom string parameter being passed to the fixture. * * @note Use the macro MITK_PARAMETERIZED_TEST_1 only if you know what * you are doing. If you are not sure, use MITK_TEST instead. * * @see MITK_PARAMETERIZED_TEST */ #define MITK_PARAMETERIZED_TEST_1(TESTMETHOD, arg1) \ { \ std::vector args; \ args.push_back(arg1); \ MITK_PARAMETERIZED_TEST(TESTMETHOD, args) \ } /** * @brief Adds a parameterized test to the current test suite. * * @ingroup MITKTestingAPI * * Use this macro after the CPPUNIT_TEST_SUITE() macro to add test cases * which need two custom parameter. * * @param TESTMETHOD The name of the member function test. * @param arg1 A custom string parameter being passed to the fixture. * * @note Use the macro MITK_PARAMETERIZED_TEST_2 only if you know what * you are doing. If you are not sure, use MITK_TEST instead. * * @see MITK_PARAMETERIZED_TEST */ #define MITK_PARAMETERIZED_TEST_2(TESTMETHOD, arg1, arg2) \ { \ std::vector args; \ args.push_back(arg1); \ args.push_back(arg2); \ MITK_PARAMETERIZED_TEST(TESTMETHOD, args) \ } - #endif diff --git a/Core/Code/files.cmake b/Core/Code/files.cmake index ee9c44b05b..db43a5072e 100644 --- a/Core/Code/files.cmake +++ b/Core/Code/files.cmake @@ -1,419 +1,420 @@ set(H_FILES Algorithms/itkImportMitkImageContainer.h Algorithms/itkImportMitkImageContainer.txx Algorithms/itkMITKScalarImageToHistogramGenerator.h Algorithms/itkMITKScalarImageToHistogramGenerator.txx Algorithms/mitkInstantiateAccessFunctions.h Algorithms/mitkPixelTypeList.h Algorithms/mitkPPArithmeticDec.h Algorithms/mitkPPArgCount.h Algorithms/mitkPPCat.h Algorithms/mitkPPConfig.h Algorithms/mitkPPControlExprIIf.h Algorithms/mitkPPControlIf.h Algorithms/mitkPPControlIIf.h Algorithms/mitkPPDebugError.h Algorithms/mitkPPDetailAutoRec.h Algorithms/mitkPPDetailDMCAutoRec.h Algorithms/mitkPPExpand.h Algorithms/mitkPPFacilitiesEmpty.h Algorithms/mitkPPFacilitiesExpand.h Algorithms/mitkPPLogicalBool.h Algorithms/mitkPPRepetitionDetailDMCFor.h Algorithms/mitkPPRepetitionDetailEDGFor.h Algorithms/mitkPPRepetitionDetailFor.h Algorithms/mitkPPRepetitionDetailMSVCFor.h Algorithms/mitkPPRepetitionFor.h Algorithms/mitkPPSeqElem.h Algorithms/mitkPPSeqForEach.h Algorithms/mitkPPSeqForEachProduct.h Algorithms/mitkPPSeq.h Algorithms/mitkPPSeqEnum.h Algorithms/mitkPPSeqSize.h Algorithms/mitkPPSeqToTuple.h Algorithms/mitkPPStringize.h Algorithms/mitkPPTupleEat.h Algorithms/mitkPPTupleElem.h Algorithms/mitkPPTupleRem.h Algorithms/mitkClippedSurfaceBoundsCalculator.h Algorithms/mitkExtractSliceFilter.h Algorithms/mitkConvert2Dto3DImageFilter.h Algorithms/mitkPlaneClipping.h Common/mitkCommon.h Common/mitkExceptionMacro.h Common/mitkGetClassHierarchy.h DataManagement/mitkProportionalTimeGeometry.h DataManagement/mitkTimeGeometry.h DataManagement/mitkImageAccessByItk.h DataManagement/mitkImageCast.h DataManagement/mitkImagePixelAccessor.h DataManagement/mitkImagePixelReadAccessor.h DataManagement/mitkImagePixelWriteAccessor.h DataManagement/mitkImageReadAccessor.h DataManagement/mitkImageWriteAccessor.h DataManagement/mitkITKImageImport.h DataManagement/mitkITKImageImport.txx DataManagement/mitkImageToItk.h DataManagement/mitkShaderProperty.h DataManagement/mitkImageToItk.txx DataManagement/mitkTimeSlicedGeometry.h # Deprecated, empty for compatibility reasons. DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkVectorDeprecated.h DataManagement/mitkArray.h DataManagement/mitkQuaternion.h DataManagement/mitkNumericTypes.h DataManagement/mitkVector.h DataManagement/mitkPoint.h DataManagement/mitkMatrix.h Interactions/mitkEventMapperAddOn.h Interfaces/mitkIDataNodeReader.h Interfaces/mitkIFileWriter.h Interfaces/mitkIFileWriter.cpp Interfaces/mitkIFileReader.h Interfaces/mitkIFileReader.cpp Rendering/mitkLocalStorageHandler.h Rendering/Colortables/HotIron.h Rendering/Colortables/Jet.h Rendering/Colortables/PET20.h Rendering/Colortables/PETColor.h IO/mitkPixelTypeTraits.h ) set(CPP_FILES Algorithms/mitkBaseDataSource.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataStorage.cpp # DataManagement/mitkDataTree.cpp DataManagement/mitkDataNode.cpp # DataManagement/mitkDataTreeStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLine.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkVector.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkMaterial.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkShaderProperty.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp # Interactions/mitkDisplayVectorInteractorLevelWindow.cpp # legacy, prob even now unneeded # Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEvent.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventFactory.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkEventMapper.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractor.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMoveBaseDataInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkState.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkTransition.cpp Interactions/mitkWheelEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkXML2EventParser.cpp Interfaces/mitkIMimeTypeProvider.cpp Interfaces/mitkInteractionEventObserver.cpp Interfaces/mitkIShaderRepository.cpp Interfaces/mitkIPropertyAliases.cpp Interfaces/mitkIPropertyDescriptions.cpp Interfaces/mitkIPropertyExtensions.cpp Interfaces/mitkIPropertyFilters.cpp Interfaces/mitkIPersistenceService.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkAbstractFileIO.cpp IO/mitkCustomMimeType.cpp IO/mitkDicomSeriesReader.cpp IO/mitkDicomSR_LoadDICOMScalar.cpp IO/mitkDicomSR_LoadDICOMScalar4D.cpp IO/mitkDicomSR_LoadDICOMRGBPixel.cpp IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp IO/mitkDicomSR_ImageBlockDescriptor.cpp IO/mitkDicomSR_GantryTiltInformation.cpp IO/mitkDicomSR_SliceGroupingResult.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkIFileIO.cpp # IO/mitkIpPicGet.c IO/mitkImageGenerator.cpp IO/mitkIOConstants.cpp IO/mitkIOUtil.cpp IO/mitkIOMimeTypes.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkMimeType.cpp IO/mitkOperation.cpp # IO/mitkPicFileIOFactory.cpp # IO/mitkPicFileReader.cpp # IO/mitkPicFileWriter.cpp # IO/mitkPicHelper.cpp # IO/mitkPicVolumeTimeSeriesIOFactory.cpp # IO/mitkPicVolumeTimeSeriesReader.cpp IO/mitkPixelType.cpp IO/mitkStandardFileLocations.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkLog.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkGLMapper.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper.cpp Rendering/mitkPointSetGLMapper2D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkSurfaceGLMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVolumeDataVtkMapper3D.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp Rendering/mitkOverlay.cpp Rendering/mitkVtkOverlay.cpp Rendering/mitkVtkOverlay2D.cpp Rendering/mitkVtkOverlay3D.cpp Rendering/mitkOverlayManager.cpp Rendering/mitkAbstractOverlayLayouter.cpp Common/mitkException.cpp Common/mitkCoreObjectFactoryBase.cpp Common/mitkCoreObjectFactory.cpp Common/mitkCoreServices.cpp Internal/mitkCoreActivator.cpp + Internal/mitkDicomSeriesReaderService.cpp Internal/mitkFileReaderWriterBase.cpp Internal/mitkImageVtkLegacyIO.cpp Internal/mitkImageVtkXmlIO.cpp Internal/mitkItkImageIO.cpp Internal/mitkLegacyFileReaderService.cpp Internal/mitkLegacyFileWriterService.cpp Internal/mitkMimeTypeProvider.cpp Internal/mitkPointSetReaderService.cpp Internal/mitkPointSetWriterService.cpp Internal/mitkRawImageFileReader.cpp Internal/mitkSurfaceStlIO.cpp Internal/mitkSurfaceVtkIO.cpp Internal/mitkSurfaceVtkLegacyIO.cpp Internal/mitkSurfaceVtkXmlIO.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml ) diff --git a/Core/CppMicroServices/.travis.yml b/Core/CppMicroServices/.travis.yml index 528cc50dcc..77d8094713 100644 --- a/Core/CppMicroServices/.travis.yml +++ b/Core/CppMicroServices/.travis.yml @@ -1,58 +1,24 @@ language: cpp compiler: - gcc - clang env: - global: - - # -- BEGIN Coverity Scan ENV - - # Pre-configuration steps that should not be analyzed - - COVERITY_SCAN_BUILD_COMMAND_PREPEND="cmake -DUS_BUILD_EXAMPLES:BOOL=ON -DUS_BUILD_TESTING:BOOL=ON -DUS_ENABLE_AUTOLOADING_SUPPORT:BOOL=ON -DUS_ENABLE_THREADING_SUPPORT:BOOL=ON ." - - # The build command with all of the arguments that you would apply to a manual `cov-build` - # Usually this is the same as STANDARD_BUILD_COMMAND, exluding the automated test arguments - - COVERITY_SCAN_BUILD_COMMAND="make -j4" - - - COVERITY_SCAN_PROJECT_NAME="saschazelzer/CppMicroServices" - - # Email address for notifications related to this build - - COVERITY_SCAN_NOTIFICATION_EMAIL="s.zelzer@dkfz-heidelberg.de" - - # Regular expression selects on which branches to run analysis - # Be aware of quotas. Do not run on every branch/commit - - COVERITY_SCAN_BRANCH_PATTERN="development" - - # COVERITY_SCAN_TOKEN via "travis encrypt" using the repo's public key - - secure: "byqeazsk6vCHFoWrZJvLFy/m1KI8sDIEhW7b1yvlGpcCrXQBoDk+dtr3kLCgiYl1+gC39av++0uOJnoM7xn/qiURW65CVDWxa9Giie079M63tqDRbOoeGezhBa8DEXJjhxZ77Iijt0tTOcp/VWg87uSiHSqI+B9HwrWDKwEtSzM=" - - - COVERITY_SCAN_BUILD_URL="https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh" - - # -- END Coverity Scan ENV - matrix: - BUILD_CONFIGURATION=0 - BUILD_CONFIGURATION=1 - BUILD_CONFIGURATION=2 - BUILD_CONFIGURATION=3 - BUILD_CONFIGURATION=4 - BUILD_CONFIGURATION=5 - BUILD_CONFIGURATION=6 - BUILD_CONFIGURATION=7 -matrix: - include: - - language: cpp - compiler: gcc - env: - script: curl -s $COVERITY_SCAN_BUILD_URL | bash - branches: only: - master - development - /^release-.*$/ script: - ctest -VV -S ./cmake/usCTestScript_travis.cmake diff --git a/Core/CppMicroServices/CMakeLists.txt b/Core/CppMicroServices/CMakeLists.txt index 716b498c66..9ca7b769a9 100644 --- a/Core/CppMicroServices/CMakeLists.txt +++ b/Core/CppMicroServices/CMakeLists.txt @@ -1,434 +1,439 @@ project(CppMicroServices) set(${PROJECT_NAME}_MAJOR_VERSION 2) set(${PROJECT_NAME}_MINOR_VERSION 99) set(${PROJECT_NAME}_PATCH_VERSION 0) set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) cmake_minimum_required(VERSION 2.8) cmake_policy(VERSION 2.8) cmake_policy(SET CMP0017 NEW) #----------------------------------------------------------------------------- # Update CMake module path #------------------------------------------------------------------------------ +set(US_CMAKE_DIR ${PROJECT_SOURCE_DIR}/cmake) + set(CMAKE_MODULE_PATH - ${PROJECT_SOURCE_DIR}/cmake + ${US_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(CMakeParseArguments) include(CMakePackageConfigHelpers) include(CheckCXXSourceCompiles) +include(usFunctionAddResources) include(usFunctionCheckCompilerFlags) -include(usFunctionEmbedResources) include(usFunctionGetGccVersion) include(usFunctionGenerateModuleInit) -include(usFunctionGenerateExecutableInit) +include(usMacroCreateModule) + +if(US_BUILD_TESTING) + include(usFunctionCompileSnippets) +endif() #----------------------------------------------------------------------------- # Init output directories #----------------------------------------------------------------------------- set(US_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") set(US_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") set(US_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") foreach(_type ARCHIVE LIBRARY RUNTIME) if(NOT CMAKE_${_type}_OUTPUT_DIRECTORY) set(CMAKE_${_type}_OUTPUT_DIRECTORY ${US_${_type}_OUTPUT_DIRECTORY}) endif() endforeach() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # CMake options #----------------------------------------------------------------------------- function(us_cache_var _var_name _var_default _var_type _var_help) set(_advanced 0) set(_force) foreach(_argn ${ARGN}) if(_argn STREQUAL ADVANCED) set(_advanced 1) elseif(_argn STREQUAL FORCE) set(_force FORCE) endif() endforeach() if(US_IS_EMBEDDED) if(NOT DEFINED ${_var_name} OR _force) set(${_var_name} ${_var_default} PARENT_SCOPE) endif() else() set(${_var_name} ${_var_default} CACHE ${_var_type} "${_var_help}" ${_force}) if(_advanced) mark_as_advanced(${_var_name}) endif() endif() endfunction() # Determine if we are being build inside a larger project if(NOT DEFINED US_IS_EMBEDDED) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(US_IS_EMBEDDED 0) else() set(US_IS_EMBEDDED 1) endif() endif() # Determine the name of the install component for "SDK" artifacts. # The default is "sdk" if(NOT DEFINED US_SDK_INSTALL_COMPONENT) set(US_SDK_INSTALL_COMPONENT COMPONENT sdk) elseif(US_SDK_INSTALL_COMPONENT) set(US_SDK_INSTALL_COMPONENT COMPONENT ${US_SDK_INSTALL_COMPONENT}) endif() us_cache_var(US_ENABLE_AUTOLOADING_SUPPORT OFF BOOL "Enable module auto-loading support") us_cache_var(US_ENABLE_THREADING_SUPPORT OFF BOOL "Enable threading support") us_cache_var(US_ENABLE_DEBUG_OUTPUT OFF BOOL "Enable debug messages" ADVANCED) -us_cache_var(US_ENABLE_RESOURCE_COMPRESSION ON BOOL "Enable resource compression" ADVANCED) us_cache_var(US_BUILD_SHARED_LIBS ON BOOL "Build shared libraries") us_cache_var(US_BUILD_TESTING OFF BOOL "Build tests") us_cache_var(US_BUILD_EXAMPLES OFF BOOL "Build example projects") if(US_BUILD_TESTING) enable_testing() endif() if(WIN32 AND NOT CYGWIN) set(default_runtime_install_dir bin/) set(default_library_install_dir bin/) set(default_archive_install_dir lib/) set(default_header_install_dir include/) set(default_auxiliary_install_dir share/) else() set(default_runtime_install_dir bin/) set(default_library_install_dir lib/${PROJECT_NAME}) set(default_archive_install_dir lib/${PROJECT_NAME}) set(default_header_install_dir include/${PROJECT_NAME}) set(default_auxiliary_install_dir share/${PROJECT_NAME}) endif() us_cache_var(RUNTIME_INSTALL_DIR ${default_runtime_install_dir} STRING "Relative install location for binaries" ADVANCED) us_cache_var(LIBRARY_INSTALL_DIR ${default_library_install_dir} STRING "Relative install location for libraries" ADVANCED) us_cache_var(ARCHIVE_INSTALL_DIR ${default_archive_install_dir} STRING "Relative install location for archives" ADVANCED) us_cache_var(HEADER_INSTALL_DIR ${default_header_install_dir} STRING "Relative install location for headers" ADVANCED) us_cache_var(AUXILIARY_INSTALL_DIR ${default_auxiliary_install_dir} STRING "Relative install location for auxiliary files" ADVANCED) set(AUXILIARY_CMAKE_INSTALL_DIR ${AUXILIARY_INSTALL_DIR}/cmake) us_cache_var(US_NAMESPACE "us" STRING "The namespace for the C++ Micro Services symbols") set(BUILD_SHARED_LIBS ${US_BUILD_SHARED_LIBS}) -set(US_MODULE_INIT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/usModuleInit.cpp" CACHE INTERNAL "The module initialization template code") -set(US_EXECUTABLE_INIT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/usExecutableInit.cpp" CACHE INTERNAL "The executable initialization template code") +set(US_MODULE_INIT_TEMPLATE "${US_CMAKE_DIR}/usModuleInit.cpp" CACHE INTERNAL "The module initialization template code") +set(US_CMAKE_RESOURCE_DEPENDENCIES_CPP "${US_CMAKE_DIR}/usCMakeResourceDependencies.cpp" CACHE INTERNAL "The dummy resource dependencies code") #----------------------------------------------------------------------------- # US C/CXX Flags #----------------------------------------------------------------------------- if(US_IS_EMBEDDED) set(CMAKE_C_FLAGS) set(CMAKE_C_FLAGS_RELEASE) set(CMAKE_C_FLAGS_DEBUG) set(CMAKE_CXX_FLAGS) set(CMAKE_CXX_FLAGS_RELEASE) set(CMAKE_CXX_FLAGS_DEBUG) set(CMAKE_LINK_FLAGS) set(CMAKE_LINK_FLAGS_RELEASE) set(CMAKE_LINK_FLAGS_DEBUG) endif() # Set C++ compiler flags if(NOT MSVC) foreach(_cxxflag -Werror -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast -Wstrict-null-sentinel -Wsign-promo -fdiagnostics-show-option) usFunctionCheckCompilerFlags(${_cxxflag} US_CXX_FLAGS) endforeach() endif() set(US_HAVE_VISIBILITY_ATTRIBUTE 0) usFunctionCheckCompilerFlags("-fvisibility=hidden -fvisibility-inlines-hidden" _have_visibility) if(_have_visibility) set(US_HAVE_VISIBILITY_ATTRIBUTE 1) endif() if(CMAKE_COMPILER_IS_GNUCXX) usFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) if(${GCC_VERSION} VERSION_LESS "4.0.0") message(FATAL_ERROR "gcc version ${GCC_VERSION} not supported. Please use gcc >= 4.") endif() # With older versions of gcc the flag -fstack-protector-all requires an extra dependency to libssp.so. # If the gcc version is lower than 4.4.0 and the build type is Release let's not include the flag. if(${GCC_VERSION} VERSION_GREATER "4.4.0" OR (CMAKE_BUILD_TYPE STREQUAL "Debug" AND ${GCC_VERSION} VERSION_LESS "4.4.0")) usFunctionCheckCompilerFlags("-fstack-protector-all" US_CXX_FLAGS) endif() if(MINGW) # suppress warnings about auto imported symbols set(US_CXX_FLAGS "-Wl,--enable-auto-import ${US_CXX_FLAGS}") # we need to define a Windows version set(US_CXX_FLAGS "-D_WIN32_WINNT=0x0500 ${US_CXX_FLAGS}") else() # Enable visibility support (only for gcc >= 4.5) # We only support hidden visibility with gcc for now. # # Clang has troubles with correctly marking template declarations and explicit template # instantiations as exported across shared library boundaries. Specifically, comparing # type_info objects from STL types does not work (used in us::Any::Type() == typeid(std::string)) # which could be related to the default visibility of STL types declared in libstdc++ and/or libc++ # but also to using RTLD_LOCAL or RTLD_GLOBAL when loading shared libraries via dlopen(). # # See http://comments.gmane.org/gmane.comp.compilers.clang.scm/50028 # and http://llvm.org/bugs/show_bug.cgi?id=10113 # if(_have_visibility AND NOT ${GCC_VERSION} VERSION_LESS "4.5") set(US_CXX_FLAGS "${US_CXX_FLAGS} ${_have_visibility}") else() set(US_GCC_RTTI_WORKAROUND_NEEDED 1) endif() endif() usFunctionCheckCompilerFlags("-O1 -D_FORTIFY_SOURCE=2" _fortify_source_flag) if(_fortify_source_flag) set(US_CXX_FLAGS_RELEASE "${US_CXX_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2") endif() endif() if(MSVC) set(US_CXX_FLAGS "/MP /WX /wd4996 /wd4251 /wd4503 ${US_CXX_FLAGS}") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${US_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${US_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${US_C_FLAGS}") set(CMAKE_C_FLAGS_REALEASE "${CMAKE_C_FLAGS_RELEASE} ${US_C_FLAGS_RELEASE}") #----------------------------------------------------------------------------- # US Link Flags #----------------------------------------------------------------------------- set(US_LINK_FLAGS ) if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) foreach(_linkflag -Wl,--no-undefined) set(_add_flag) usFunctionCheckCompilerFlags("${_linkflag}" _add_flag) if(_add_flag) set(US_LINK_FLAGS "${US_LINK_FLAGS} ${_linkflag}") endif() endforeach() endif() #----------------------------------------------------------------------------- # US Header Checks #----------------------------------------------------------------------------- include(CheckIncludeFileCXX) include(CheckCXXSourceCompiles) +CHECK_INCLUDE_FILE_CXX(cxxabi.h US_HAVE_CXXABI_H) CHECK_INCLUDE_FILE_CXX(stdint.h US_HAVE_STDINT_H) CHECK_INCLUDE_FILE_CXX(tr1/unordered_map US_HAVE_TR1_UNORDERED_MAP_H) CHECK_INCLUDE_FILE_CXX(tr1/unordered_set US_HAVE_TR1_UNORDERED_SET_H) CHECK_INCLUDE_FILE_CXX(tr1/functional US_HAVE_TR1_FUNCTIONAL_H) CHECK_INCLUDE_FILE_CXX(unordered_map US_HAVE_UNORDERED_MAP_H) CHECK_INCLUDE_FILE_CXX(unordered_set US_HAVE_UNORDERED_SET_H) CHECK_INCLUDE_FILE_CXX(functional US_HAVE_FUNCTIONAL_H) if(US_HAVE_UNORDERED_MAP_H) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::unordered_map m; return 0; }" US_HAVE_TR1_UNORDERED_MAP) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::unordered_map m; return 0; }" US_HAVE_STD_UNORDERED_MAP) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::hash(); return 0; }" US_HAVE_TR1_HASH) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::hash(); return 0; }" US_HAVE_STD_HASH) if(US_HAVE_STD_HASH) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend struct std::hash; }; int main() { return 0; }" US_HAVE_STD_HASH_STRUCT) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend class std::hash; }; int main() { return 0; }" US_HAVE_STD_HASH_CLASS) elseif(US_HAVE_TR1_HASH) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend struct std::tr1::hash; }; int main() { return 0; }" US_HAVE_TR1_HASH_STRUCT) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend class std::tr1::hash; }; int main() { return 0; }" US_HAVE_TR1_HASH_CLASS) endif() elseif(US_HAVE_TR1_UNORDERED_MAP_H) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::unordered_map m; return 0; }" US_HAVE_TR1_UNORDERED_MAP) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::unordered_map m; return 0; }" US_HAVE_STD_UNORDERED_MAP) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::hash(); return 0; }" US_HAVE_TR1_HASH) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::hash(); return 0; }" US_HAVE_STD_HASH) if(US_HAVE_STD_HASH) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend struct std::hash; }; int main() { return 0; }" US_HAVE_STD_HASH_STRUCT) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend class std::hash; }; int main() { return 0; }" US_HAVE_STD_HASH_CLASS) elseif(US_HAVE_TR1_HASH) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend struct std::tr1::hash; }; int main() { return 0; }" US_HAVE_TR1_HASH_STRUCT) CHECK_CXX_SOURCE_COMPILES("#include \nstruct A { friend class std::tr1::hash; }; int main() { return 0; }" US_HAVE_TR1_HASH_CLASS) endif() else() message(SEND_ERROR "The header file \"unordered_map\" is not available.") endif() if(NOT (US_HAVE_TR1_UNORDERED_MAP OR US_HAVE_STD_UNORDERED_MAP)) message(SEND_ERROR "The \"unordered_map\" type is not available.") endif() if(US_HAVE_UNORDERED_SET_H) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::unordered_set s; return 0; }" US_HAVE_TR1_UNORDERED_SET) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::unordered_set s; return 0; }" US_HAVE_STD_UNORDERED_SET) elseif(US_HAVE_TR1_UNORDERED_SET_H) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::unordered_set s; return 0; }" US_HAVE_TR1_UNORDERED_SET) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::unordered_set s; return 0; }" US_HAVE_STD_UNORDERED_SET) else() message(SEND_ERROR "The header file \"unordered_set\" is not available.") endif() if(NOT (US_HAVE_TR1_UNORDERED_SET OR US_HAVE_STD_UNORDERED_SET)) message(SEND_ERROR "The \"unordered_set\" type is not available.") endif() if(NOT (US_HAVE_FUNCTIONAL_H OR US_HAVE_TR1_FUNCTIONAL_H)) message(SEND_ERROR "The header file \"functional\" is not available.") endif() if(US_HAVE_FUNCTIONAL_H) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::function f(main); return 0; }" US_HAVE_TR1_FUNCTION) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::function f(main); return 0; }" US_HAVE_STD_FUNCTION) endif() if((NOT (US_HAVE_STD_FUNCTION OR US_HAVE_TR1_FUNCTION)) AND US_HAVE_TR1_FUNCTIONAL_H) unset(US_HAVE_TR1_FUNCTION CACHE) unset(US_HAVE_STD_FUNCTION CACHE) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::tr1::function f(main); return 0; }" US_HAVE_TR1_FUNCTION) CHECK_CXX_SOURCE_COMPILES("#include \nint main() { std::function f(main); return 0; }" US_HAVE_STD_FUNCTION) endif() if(NOT (US_HAVE_STD_FUNCTION OR US_HAVE_TR1_FUNCTION)) message(SEND_ERROR "The \"function\" type is not available.") endif() #----------------------------------------------------------------------------- # Source directory #----------------------------------------------------------------------------- -set(US_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include) +set(US_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/third_party ${PROJECT_BINARY_DIR}/include) # Configure CppMicroServicesConfig.cmake for the build tree. # The file is used in sub-directories. set(PACKAGE_CONFIG_INCLUDE_DIR ${US_INCLUDE_DIRS}) set(PACKAGE_CONFIG_RUNTIME_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -set(PACKAGE_CONFIG_CMAKE_DIR ${PROJECT_SOURCE_DIR}/cmake) +set(PACKAGE_CONFIG_CMAKE_DIR ${US_CMAKE_DIR}) set(US_RCC_EXECUTABLE_NAME usResourceCompiler CACHE INTERNAL "The target name of the usResourceCompiler executable.") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY ) set(us_global_config_h_file "${PROJECT_BINARY_DIR}/include/usGlobalConfig.h") -configure_file(${PROJECT_SOURCE_DIR}/cmake/usGlobalConfig.h.in ${us_global_config_h_file}) +configure_file(${US_CMAKE_DIR}/usGlobalConfig.h.in ${us_global_config_h_file}) include_directories(${US_INCLUDE_DIRS}) add_subdirectory(tools) add_subdirectory(core) #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- add_subdirectory(doc) #----------------------------------------------------------------------------- # Last configuration and install steps #----------------------------------------------------------------------------- export(TARGETS ${US_RCC_EXECUTABLE_NAME} FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake) install(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Targets.cmake DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR}) set(_install_cmake_scripts ${US_MODULE_INIT_TEMPLATE} - ${US_EXECUTABLE_INIT_TEMPLATE} - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeParseArguments.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPackageHandleStandardArgs.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPackageMessage.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/usFunctionGenerateModuleInit.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/usFunctionGenerateExecutableInit.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/usFunctionEmbedResources.cmake + ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} + ${US_CMAKE_DIR}/CMakeParseArguments.cmake + ${US_CMAKE_DIR}/FindPackageHandleStandardArgs.cmake + ${US_CMAKE_DIR}/FindPackageMessage.cmake + ${US_CMAKE_DIR}/usFunctionGenerateModuleInit.cmake + ${US_CMAKE_DIR}/usFunctionAddResources.cmake ) install(FILES ${_install_cmake_scripts} DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR}) install(FILES ${us_global_config_h_file} DESTINATION ${HEADER_INSTALL_DIR}) # Configure CppMicroServicesConfig.cmake for the install tree set(CONFIG_INCLUDE_DIR ${HEADER_INSTALL_DIR}) set(CONFIG_RUNTIME_DIR ${RUNTIME_INSTALL_DIR}) set(CONFIG_CMAKE_DIR ${AUXILIARY_CMAKE_INSTALL_DIR}) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake INSTALL_DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR} PATH_VARS CONFIG_INCLUDE_DIR CONFIG_RUNTIME_DIR CONFIG_CMAKE_DIR NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO ) # Version information configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake @ONLY ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} ) diff --git a/Core/CppMicroServices/CppMicroServicesConfig.cmake.in b/Core/CppMicroServices/CppMicroServicesConfig.cmake.in index a69bde8f39..d770025474 100644 --- a/Core/CppMicroServices/CppMicroServicesConfig.cmake.in +++ b/Core/CppMicroServices/CppMicroServicesConfig.cmake.in @@ -1,192 +1,199 @@ # A package config for @PROJECT_NAME@. # This file loads component specific configuration files and # sets the following variables which can be used in other # CMake projects to build and link against @PROJECT_NAME@: # # US_INCLUDE_DIRS # US_LIBRARIES # US_RUNTIME_LIBRARY_DIRS # # The following variables are aliases for the ones above: # # CppMicroServices_INCLUDE_DIRS # CppMicroServices_LIBRARIES # CppMicroServices_RUNTIME_LIBRARY_DIRS # # To specify a compatible version for a specific component, # set the following variable before calling find_package: # # US__FIND_VERSION # # After find_package returns successfully, the following additional # variables will be set: # # US_FOUND # CPPMICROSERVICES_FOUND # # US_RCC_EXECUTABLE # # US_CXX_FLAGS # US_CXX_FLAGS_RELEASE # US_CXX_FLAGS_DEBUG # US_C_FLAGS # US_C_FLAGS_RELEASE # US_C_FLAGS_DEBUG # US_LINK_FLAGS # US_LINK_FLAGS_RELEASE # US_LINK_FLAGS_DEBUG # US_COMPILE_DEFINITIONS # US_COMPILE_DEFINITIONS_RELEASE # US_COMPILE_DEFINITIONS_DEBUG # # US_VERSION # US_VERSION_MAJOR # US_VERSION_MINOR # US_VERSION_PATCH # US_VERSION_TWEAK # US_VERSION_COUNT # # Additional component specific variables: # # US__FOUND # # US__VERSION # US__VERSION_MAJOR # US__VERSION_MINOR # US__VERSION_PATCH # US__VERSION_TWEAK # US__VERSION_COUNT # # US__CXX_FLAGS # US__CXX_FLAGS_RELEASE # US__CXX_FLAGS_DEBUG # US__C_FLAGS # US__C_FLAGS_RELEASE # US__C_FLAGS_DEBUG # US__LINK_FLAGS # US__LINK_FLAGS_RELEASE # US__LINK_FLAGS_DEBUG # US__COMPILE_DEFINITIONS # US__COMPILE_DEFINITIONS_RELEASE # US__COMPILE_DEFINITIONS_DEBUG # @PACKAGE_INIT@ list(APPEND CMAKE_MODULE_PATH "@PACKAGE_CONFIG_CMAKE_DIR@") set(US_RCC_EXECUTABLE_NAME @US_RCC_EXECUTABLE_NAME@) set(US_MODULE_INIT_TEMPLATE "@PACKAGE_CONFIG_CMAKE_DIR@/usModuleInit.cpp") -set(US_EXECUTABLE_INIT_TEMPLATE "@PACKAGE_CONFIG_CMAKE_DIR@/usExecutableInit.cpp") +set(US_CMAKE_RESOURCE_DEPENDENCIES_CPP "@PACKAGE_CONFIG_CMAKE_DIR@/usCMakeResourceDependencies.cpp") # The CppMicroServices resource compiler find_program(US_RCC_EXECUTABLE ${US_RCC_EXECUTABLE_NAME} PATHS "@PACKAGE_CONFIG_RUNTIME_DIR@" PATH_SUFFIXES Release Debug RelWithDebInfo MinSizeRel) mark_as_advanced(US_RCC_EXECUTABLE) # Include helper macros include(CMakeParseArguments) if(CMAKE_VERSION VERSION_LESS "2.8.8") # We need the HANDLE_COMPONENTS argument include("@PACKAGE_CONFIG_CMAKE_DIR@/FindPackageHandleStandardArgs.cmake") elseif(NOT COMMAND find_package_handle_standard_args) include(FindPackageHandleStandardArgs) endif() include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionGenerateModuleInit.cmake") -include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionGenerateExecutableInit.cmake") -include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionEmbedResources.cmake") +include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionAddResources.cmake") # Global include directory set(US_INCLUDE_DIRS "@PACKAGE_CONFIG_INCLUDE_DIR@") # Clear variables set(US_LIBRARIES ) set(US_RUNTIME_LIBRARY_DIRS ) set(US_CXX_FLAGS ) set(US_CXX_FLAGS_RELEASE ) set(US_CXX_FLAGS_DEBUG ) set(US_C_FLAGS ) set(US_C_FLAGS_RELEASE ) set(US_C_FLAGS_DEBUG ) set(US_LINK_FLAGS ) set(US_LINK_FLAGS_RELEASE ) set(US_LINK_FLAGS_DEBUG ) set(US_COMPILE_DEFINITIONS ) set(US_COMPILE_DEFINITIONS_RELEASE ) set(US_COMPILE_DEFINITIONS_DEBUG ) # Components support set(US_MODULES Core) # ConfigAdmin EventAdmin ... +set(US_Core_MODULE_DEPS ) + if(NOT @PROJECT_NAME@_FIND_COMPONENTS) set(@PROJECT_NAME@_FIND_COMPONENTS Core) endif() -set(@PROJECT_NAME@_FOUND TRUE) +set(US_MODULES_NEEDED ) foreach(component ${@PROJECT_NAME@_FIND_COMPONENTS}) + list(APPEND US_MODULES_NEEDED ${US_${component}_MODULE_DEPS} ${component}) +endforeach() +list(REMOVE_DUPLICATES US_MODULES_NEEDED) + +set(@PROJECT_NAME@_FOUND TRUE) +foreach(component ${US_MODULES_NEEDED}) if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/us${component}Config.cmake") set(US_${component}_FOUND 0) set(CppMicroServices_${component}_FOUND 0) else() find_package(us${component} ${US_${component}_FIND_VERSION} QUIET REQUIRED HINTS ${CMAKE_CURRENT_LIST_DIR} NO_DEFAULT_PATH ) mark_as_advanced(us${component}_DIR) set(US_${component}_FOUND ${us${component}_FOUND}) set(CppMicroServices_${component}_FOUND ${US_${component}_FOUND}) endif() if(US_${component}_FOUND) list(APPEND US_INCLUDE_DIRS ${US_${component}_INCLUDE_DIRS}) list(APPEND US_LIBRARIES ${US_${component}_LIBRARIES}) list(APPEND US_RUNTIME_LIBRARY_DIRS ${US_${component}_RUNTIME_LIBRARY_DIRS}) set(US_CXX_FLAGS "${US_CXX_FLAGS} ${US_${component}_CXX_FLAGS}") set(US_CXX_FLAGS_RELEASE "${US_CXX_FLAGS_RELEASE} ${US_${component}_CXX_FLAGS_RELEASE}") set(US_CXX_FLAGS_DEBUG "${US_CXX_FLAGS_DEBUG} ${US_${component}_CXX_FLAGS_DEBUG}") set(US_C_FLAGS "${US_C_FLAGS} ${US_${component}_C_FLAGS}") set(US_C_FLAGS_RELEASE "${US_C_FLAGS_RELEASE} ${US_${component}_C_FLAGS_RELEASE}") set(US_C_FLAGS_DEBUG "${US_C_FLAGS_DEBUG} ${US_${component}_C_FLAGS_DEBUG}") set(US_LINK_FLAGS "${US_LINK_FLAGS} ${US_${component}_LINK_FLAGS}") set(US_LINK_FLAGS_RELEASE "${US_LINK_FLAGS_RELEASE} ${US_${component}_LINK_FLAGS_RELEASE}") set(US_LINK_FLAGS_DEBUG "${US_LINK_FLAGS_DEBUG} ${US_${component}_LINK_FLAGS_DEBUG}") set(US_COMPILE_DEFINITIONS "${US_COMPILE_DEFINITIONS} ${US_${component}_COMPILE_DEFINITIONS}") set(US_COMPILE_DEFINITIONS_RELEASE "${US_COMPILE_DEFINITIONS_RELEASE} ${US_${component}_COMPILE_DEFINITIONS_RELEASE}") set(US_COMPILE_DEFINITIONS_DEBUG "${US_COMPILE_DEFINITIONS_DEBUG} ${US_${component}_COMPILE_DEFINITIONS_DEBUG}") set(US_${component}_VERSION ${${component}_VERSION}) set(US_${component}_VERSION_MAJOR ${${component}_VERSION_MAJOR}) set(US_${component}_VERSION_MINOR ${${component}_VERSION_MINOR}) set(US_${component}_VERSION_PATCH ${${component}_VERSION_PATCH}) set(US_${component}_VERSION_TWEAK ${${component}_VERSION_TWEAK}) set(US_${component}_VERSION_COUNT ${${component}_VERSION_COUNT}) else() if(@PROJECT_NAME@_FIND_REQUIRED_${component}) set(@PROJECT_NAME@_FOUND FALSE) endif() endif() endforeach() if(US_INCLUDE_DIRS) list(REMOVE_DUPLICATES US_INCLUDE_DIRS) endif() if(US_LIBRARIES) list(REMOVE_DUPLICATES US_LIBRARIES) endif() if(US_RUNTIME_LIBRARY_DIRS) list(REMOVE_DUPLICATES US_RUNTIME_LIBRARY_DIRS) endif() set(CppMicroServices_INCLUDE_DIRS ${US_INCLUDE_DIRS}) set(CppMicroServices_LIBRARIES ${US_LIBRARIES}) set(CppMicroServices_RUNTIME_LIBRARY_DIRS ${US_RUNTIME_LIBRARY_DIRS}) set(@PROJECT_NAME@_CONFIG ${CMAKE_CURRENT_LIST_FILE}) find_package_handle_standard_args(@PROJECT_NAME@ HANDLE_COMPONENTS CONFIG_MODE ) string(TOUPPER "@PROJECT_NAME@" @PROJECT_NAME@_UPPER) set(US_FOUND ${${@PROJECT_NAME@_UPPER}_FOUND}) diff --git a/Core/CppMicroServices/cmake/usExecutableInit.cpp b/Core/CppMicroServices/cmake/usCMakeResourceDependencies.cpp similarity index 79% rename from Core/CppMicroServices/cmake/usExecutableInit.cpp rename to Core/CppMicroServices/cmake/usCMakeResourceDependencies.cpp index 6e43c3d884..c1aa3719ea 100644 --- a/Core/CppMicroServices/cmake/usExecutableInit.cpp +++ b/Core/CppMicroServices/cmake/usCMakeResourceDependencies.cpp @@ -1,24 +1,30 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ -#include +#include -US_INITIALIZE_EXECUTABLE("@US_EXECUTABLE_IDENTIFIER@") +namespace { + +// This is dummy code to silence some linkers warning about +// empty object files. +struct CMakeResourceDependencies { CMakeResourceDependencies() { std::cout << std::flush; } }; + +} diff --git a/Core/CppMicroServices/cmake/usCTestScript.cmake b/Core/CppMicroServices/cmake/usCTestScript.cmake index 29a4b57aeb..eddc8c2b01 100644 --- a/Core/CppMicroServices/cmake/usCTestScript.cmake +++ b/Core/CppMicroServices/cmake/usCTestScript.cmake @@ -1,102 +1,109 @@ macro(build_and_test) set(CTEST_SOURCE_DIRECTORY ${US_SOURCE_DIR}) set(CTEST_BINARY_DIRECTORY "${CTEST_DASHBOARD_ROOT}/${CTEST_PROJECT_NAME}_${CTEST_DASHBOARD_NAME}") #if(NOT CTEST_BUILD_NAME) # set(CTEST_BUILD_NAME "${CMAKE_SYSTEM}_${CTEST_COMPILER}_${CTEST_DASHBOARD_NAME}") #endif() ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY}) ctest_start("Experimental") if(NOT EXISTS "${CTEST_BINARY_DIRECTORY}/CMakeCache.txt") file(WRITE "${CTEST_BINARY_DIRECTORY}/CMakeCache.txt" "${CTEST_INITIAL_CACHE}") endif() ctest_configure(RETURN_VALUE res) if (res) message(FATAL_ERROR "CMake configure error") endif() ctest_build(RETURN_VALUE res) if (res) message(FATAL_ERROR "CMake build error") endif() ctest_test(RETURN_VALUE res PARALLEL_LEVEL ${CTEST_PARALLEL_LEVEL}) if (res) message(FATAL_ERROR "CMake test error") endif() if(WITH_MEMCHECK AND CTEST_MEMORYCHECK_COMMAND) ctest_memcheck() endif() if(WITH_COVERAGE AND CTEST_COVERAGE_COMMAND) ctest_coverage() endif() #ctest_submit() endmacro() function(create_initial_cache var _shared _threading _autoload) set(_initial_cache " US_BUILD_TESTING:BOOL=ON US_BUILD_SHARED_LIBS:BOOL=${_shared} US_ENABLE_THREADING_SUPPORT:BOOL=${_threading} US_ENABLE_AUTOLOADING_SUPPORT:BOOL=${_autoload} ") if(_shared) set(_initial_cache "${_initial_cache} US_BUILD_EXAMPLES:BOOL=ON ") endif() set(${var} ${_initial_cache} PARENT_SCOPE) if(_shared) set(CTEST_DASHBOARD_NAME "shared") else() set(CTEST_DASHBOARD_NAME "static") endif() if(_threading) set(CTEST_DASHBOARD_NAME "${CTEST_DASHBOARD_NAME}-threading") endif() if(_autoload) set(CTEST_DASHBOARD_NAME "${CTEST_DASHBOARD_NAME}-autoloading") endif() - set(CTEST_DASHBOARD_NAME ${CTEST_DASHBOARD_NAME} PARENT_SCOPE) + set(CTEST_DASHBOARD_NAME "${CTEST_DASHBOARD_NAME} (${_generator})" PARENT_SCOPE) endfunction() #========================================================= set(CTEST_PROJECT_NAME CppMicroServices) if(NOT CTEST_PARALLEL_LEVEL) set(CTEST_PARALLEL_LEVEL 1) endif() # SHARED THREADING AUTOLOAD set(config0 0 0 0 ) set(config1 0 0 1 ) set(config2 0 1 0 ) set(config3 0 1 1 ) set(config4 1 0 0 ) set(config5 1 0 1 ) set(config6 1 1 0 ) set(config7 1 1 1 ) -foreach(i ${US_BUILD_CONFIGURATION}) - create_initial_cache(CTEST_INITIAL_CACHE ${config${i}}) - message("Testing build configuration: ${CTEST_DASHBOARD_NAME}") - build_and_test() +if(NOT US_CMAKE_GENERATOR) + set(US_CMAKE_GENERATOR "Unix Makefiles") +endif() + +foreach (_generator ${US_CMAKE_GENERATOR}) + set(CTEST_CMAKE_GENERATOR ${_generator}) + foreach(i ${US_BUILD_CONFIGURATION}) + create_initial_cache(CTEST_INITIAL_CACHE ${config${i}}) + message("Testing build configuration: ${CTEST_DASHBOARD_NAME}") + build_and_test() + endforeach() endforeach() diff --git a/Core/CppMicroServices/cmake/usCTestScript_custom.cmake b/Core/CppMicroServices/cmake/usCTestScript_custom.cmake index ee9908c8bd..9b95df48b6 100644 --- a/Core/CppMicroServices/cmake/usCTestScript_custom.cmake +++ b/Core/CppMicroServices/cmake/usCTestScript_custom.cmake @@ -1,24 +1,38 @@ find_program(CTEST_COVERAGE_COMMAND NAMES gcov) find_program(CTEST_MEMORYCHECK_COMMAND NAMES valgrind) find_program(CTEST_GIT_COMMAND NAMES git) set(CTEST_SITE "bigeye") -set(CTEST_DASHBOARD_ROOT "/tmp/us builds") -#set(CTEST_COMPILER "gcc-4.5") -set(CTEST_CMAKE_GENERATOR "Unix Makefiles") -set(CTEST_BUILD_FLAGS "-j") -set(CTEST_BUILD_CONFIGURATION Debug) +if(WIN32) + set(CTEST_DASHBOARD_ROOT "C:/tmp/us") +else() + set(CTEST_DASHBOARD_ROOT "/tmp/us") + set(CTEST_BUILD_FLAGS "-j") + #set(CTEST_COMPILER "gcc-4.5") +endif() + +set(CTEST_CONFIGURATION_TYPE Release) +set(CTEST_BUILD_CONFIGURATION Release) + set(CTEST_PARALLEL_LEVEL 4) set(US_TEST_SHARED 1) set(US_TEST_STATIC 1) set(US_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../") set(US_BUILD_CONFIGURATION ) foreach(i RANGE 7) list(APPEND US_BUILD_CONFIGURATION ${i}) endforeach() +if(WIN32 AND NOT MINGW) + set(US_CMAKE_GENERATOR + "Visual Studio 9 2008" + "Visual Studio 10" + "Visual Studio 11" + ) +endif() + include(${US_SOURCE_DIR}/cmake/usCTestScript.cmake) diff --git a/Core/CppMicroServices/cmake/usFunctionAddResources.cmake b/Core/CppMicroServices/cmake/usFunctionAddResources.cmake new file mode 100644 index 0000000000..e0b9e7215f --- /dev/null +++ b/Core/CppMicroServices/cmake/usFunctionAddResources.cmake @@ -0,0 +1,160 @@ +#! \ingroup MicroServicesCMake +#! \brief Add resources to a library or executable. +#! +#! This CMake function uses an external command line program to generate a ZIP archive +#! containing data from external resources such as text files or images or other ZIP +#! archives. The created archive file is appended as a binary blob to the target file. +#! +#! Each module can call this function to add resources and make them available at +#! runtime through the Module class. Multiple calls to this function append the +#! input files to the target file. +#! +#! \note To set-up correct file dependencies from your module target to your resource +#! files, you have to add a file named \e $_resources.cpp +#! to the source list of the target (or the value provided to the SOURCE_OUTPUT +#! parameter). This ensures that changed resource files will automatically be +#! re-added to the module. +#! +#! In the case of linking static modules which contain resources to the target module, +#! adding the static module target name to the ZIP_ARCHIVES list will merge its +#! resources into the target module. +#! +#! Example usage: +#! \code{.cmake} +#! set(module_srcs ) +#! usFunctionAddResources(TARGET mylib +#! MODULE_NAME org_me_mylib +#! FILES config.properties logo.png +#! ) +#! \endcode +#! +#! \param TARGET (required) The target to which the resource files are added. +#! \param MODULE_NAME (required/optional) The module name of the target, as specified in +#! the \c US_MODULE_NAME pre-processor definition of that target. This parameter +#! is optional if a target property with the name US_MODULE_NAME exists, containing +#! the required module name. +#! \param SOURCE_OUTPUT (optional) The name for a generated source which can be included in +#! the source list of the TARGET to set-up resource dependencies. If empty, the value +#! defautls to ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_resources.cpp. If a relative path +#! is given, it will be appended to the current binary directory. Setting this parameter +#! to a non-default value for repeated calls of this macro for the same target is +#! recommended. +#! \param COMPRESSION_LEVEL (optional) The zip compression level (0-9). Defaults to the default zip +#! level. Level 0 disables compression. +#! \param WORKING_DIRECTORY (optional) The root path for all resource files listed after the +#! FILES argument. If no or a relative path is given, it is considered relativ to the +#! current CMake source directory. +#! \param FILES (optional) A list of resource files (paths to external files in the file system) +#! relative to the current working directory. +#! \param ZIP_ARCHIVES (optional) A list of zip archives (relative to the current working directory +#! or absolute file paths) whose contents is merged into the target module. If a list entry +#! is a valid target name and that target is a static library, its absolute file path is +#! used instead. +#! +function(usFunctionAddResources) + + cmake_parse_arguments(US_RESOURCE "" "TARGET;MODULE_NAME;SOURCE_OUTPUT;WORKING_DIRECTORY;COMPRESSION_LEVEL" "FILES;ZIP_ARCHIVES" ${ARGN}) + + if(NOT US_RESOURCE_TARGET) + message(SEND_ERROR "TARGET argument not specified.") + endif() + + if(NOT US_RESOURCE_MODULE_NAME) + get_target_property(US_RESOURCE_MODULE_NAME ${US_RESOURCE_TARGET} US_MODULE_NAME) + if(NOT US_RESOURCE_MODULE_NAME) + message(SEND_ERROR "Either the MODULE_NAME argument or the US_MODULE_NAME target property is required.") + endif() + endif() + + if(NOT US_RESOURCE_SOURCE_OUTPUT) + set(US_RESOURCE_SOURCE_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_TARGET}_resources.cpp") + elseif(NOT IS_ABSOLUTE ${US_RESOURCE_SOURCE_OUTPUT}) + set(US_RESOURCE_SOURCE_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_SOURCE_OUTPUT}") + endif() + + if(NOT US_RESOURCE_FILES AND NOT US_RESOURCE_ZIP_ARCHIVES) + message(WARNING "No resources specified. Skipping resource processing.") + return() + endif() + + if(NOT US_RESOURCE_WORKING_DIRECTORY) + set(US_RESOURCE_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + if(NOT IS_ABSOLUTE ${US_RESOURCE_WORKING_DIRECTORY}) + set(US_RESOURCE_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${US_RESOURCE_WORKING_DIRECTORY}") + endif() + + if(US_RESOURCE_COMPRESSION_LEVEL) + set(cmd_line_args -${US_RESOURCE_COMPRESSION_LEVEL}) + endif() + + set(resource_compiler ${US_RCC_EXECUTABLE}) + if(TARGET ${US_RCC_EXECUTABLE_NAME}) + set(resource_compiler ${US_RCC_EXECUTABLE_NAME}) + elseif(NOT resource_compiler) + message(FATAL_ERROR "The CppMicroServices resource compiler was not found. Check the US_RCC_EXECUTABLE CMake variable.") + endif() + + set(_cmd_deps ) + foreach(_file ${US_RESOURCE_FILES}) + if(IS_ABSOLUTE ${_file}) + list(APPEND _cmd_deps ${_file}) + else() + list(APPEND _cmd_deps ${US_RESOURCE_WORKING_DIRECTORY}/${_file}) + endif() + endforeach() + + set(_zip_args ) + if(US_RESOURCE_ZIP_ARCHIVES) + foreach(_zip_archive ${US_RESOURCE_ZIP_ARCHIVES}) + if(TARGET ${_zip_archive}) + get_target_property(_is_static_lib ${_zip_archive} TYPE) + if(_is_static_lib STREQUAL "STATIC_LIBRARY") + list(APPEND _cmd_deps ${_zip_archive}) + list(APPEND _zip_args $) + endif() + else() + if(IS_ABSOLUTE ${_zip_archive}) + list(APPEND _cmd_deps ${_zip_archive}) + else() + list(APPEND _cmd_deps ${US_RESOURCE_WORKING_DIRECTORY}/${_zip_archive}) + endif() + list(APPEND _zip_args ${_zip_archive}) + endif() + endforeach() + endif() + + if(NOT US_RESOURCE_FILES AND NOT _zip_args) + return() + endif() + + if(US_RESOURCE_FILES) + set(_file_args -a ${US_RESOURCE_FILES}) + endif() + if(_zip_args) + set(_zip_args -m ${_zip_args}) + endif() + + # This command depends on the given resource files and creates a source + # file which must be added to the source list of the related target. + # This way, the following command is executed if the resources change + # and it just touches the created source file to force a (actually unnecessary) + # re-linking and hence the execution of POST_BUILD commands. + add_custom_command( + OUTPUT ${US_RESOURCE_SOURCE_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E copy ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} ${US_RESOURCE_SOURCE_OUTPUT} + DEPENDS ${_cmd_deps} ${resource_compiler} + COMMENT "Checking resource dependencies for ${US_RESOURCE_TARGET}" + VERBATIM + ) + + add_custom_command( + TARGET ${US_RESOURCE_TARGET} + POST_BUILD + COMMAND ${resource_compiler} ${cmd_line_args} $ ${US_RESOURCE_MODULE_NAME} ${_file_args} ${_zip_args} + WORKING_DIRECTORY ${US_RESOURCE_WORKING_DIRECTORY} + COMMENT "Adding resources to ${US_RESOURCE_TARGET}" + VERBATIM + ) + +endfunction() diff --git a/Core/CppMicroServices/cmake/usFunctionCompileSnippets.cmake b/Core/CppMicroServices/cmake/usFunctionCompileSnippets.cmake index 6a3dcb92fd..e183c40837 100644 --- a/Core/CppMicroServices/cmake/usFunctionCompileSnippets.cmake +++ b/Core/CppMicroServices/cmake/usFunctionCompileSnippets.cmake @@ -1,48 +1,50 @@ function(usFunctionCompileSnippets snippet_path) # get all files called "main.cpp" file(GLOB_RECURSE main_cpp_list "${snippet_path}/main.cpp") foreach(main_cpp_file ${main_cpp_list}) # get the directory containing the main.cpp file get_filename_component(main_cpp_dir "${main_cpp_file}" PATH) set(snippet_src_files ) # If there exists a "files.cmake" file in the snippet directory, # include it and assume it sets the variable "snippet_src_files" # to a list of source files for the snippet. if(EXISTS "${main_cpp_dir}/files.cmake") include("${main_cpp_dir}/files.cmake") set(_tmp_src_files ${snippet_src_files}) set(snippet_src_files ) foreach(_src_file ${_tmp_src_files}) if(IS_ABSOLUTE ${_src_file}) list(APPEND snippet_src_files ${_src_file}) else() list(APPEND snippet_src_files ${main_cpp_dir}/${_src_file}) endif() endforeach() else() # glob all files in the directory and add them to the snippet src list file(GLOB_RECURSE snippet_src_files "${main_cpp_dir}/*") endif() # Uset the top-level directory name as the executable name string(REPLACE "/" ";" main_cpp_dir_tokens "${main_cpp_dir}") list(GET main_cpp_dir_tokens -1 snippet_exec_name) set(snippet_target_name "Snippet-${snippet_exec_name}") add_executable(${snippet_target_name} ${snippet_src_files}) target_link_libraries(${snippet_target_name} ${PROJECT_TARGET} ${snippet_link_libraries}) + set_property(TARGET ${snippet_target_name} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=main) + set_property(TARGET ${snippet_target_name} PROPERTY US_MODULE_NAME main) set_target_properties(${snippet_target_name} PROPERTIES LABELS Documentation RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/snippets" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/snippets" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/snippets" OUTPUT_NAME ${snippet_exec_name} ) endforeach() endfunction() diff --git a/Core/CppMicroServices/cmake/usFunctionCreateTestModule.cmake b/Core/CppMicroServices/cmake/usFunctionCreateTestModule.cmake index 11f31dfbe0..b97e3ac50d 100644 --- a/Core/CppMicroServices/cmake/usFunctionCreateTestModule.cmake +++ b/Core/CppMicroServices/cmake/usFunctionCreateTestModule.cmake @@ -1,42 +1,48 @@ macro(_us_create_test_module_helper) - if(_res_files) - usFunctionEmbedResources(_srcs LIBRARY_NAME ${name} ROOT_DIR ${_res_root} FILES ${_res_files}) - endif() - add_library(${name} ${_srcs}) - if(NOT US_BUILD_SHARED_LIBS) + set_property(TARGET ${name} + APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${name}) + set_property(TARGET ${name} PROPERTY US_MODULE_NAME ${name}) + if(NOT US_BUILD_SHARED_LIBS OR NOT BUILD_SHARED_LIBS) set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE) endif() - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") get_property(_compile_flags TARGET ${name} PROPERTY COMPILE_FLAGS) set_property(TARGET ${name} PROPERTY COMPILE_FLAGS "${_compile_flags} -fPIC") endif() - target_link_libraries(${name} ${${PROJECT_NAME}_TARGET} ${US_LINK_LIBRARIES}) + target_link_libraries(${name} ${${PROJECT_NAME}_TARGET} ${US_TEST_LINK_LIBRARIES} ${US_LINK_LIBRARIES}) - set(_us_test_module_libs "${_us_test_module_libs};${name}" CACHE INTERNAL "" FORCE) + if(_res_files OR US_TEST_LINK_LIBRARIES) + usFunctionAddResources(TARGET ${name} WORKING_DIRECTORY ${_res_root} + FILES ${_res_files} + ZIP_ARCHIVES ${US_TEST_LINK_LIBRARIES}) + endif() + + if(NOT US_TEST_SKIP_MODULE_LIST) + set(_us_test_module_libs "${_us_test_module_libs};${name}" CACHE INTERNAL "" FORCE) + endif() endmacro() function(usFunctionCreateTestModule name) set(_srcs ${ARGN}) set(_res_files ) - usFunctionGenerateModuleInit(_srcs NAME "${name} Module" LIBRARY_NAME ${name}) + usFunctionGenerateModuleInit(_srcs) _us_create_test_module_helper() endfunction() function(usFunctionCreateTestModuleWithResources name) - cmake_parse_arguments(US_TEST "" "RESOURCES_ROOT" "SOURCES;RESOURCES" "" ${ARGN}) - set(_srcs ${US_TEST_SOURCES}) + cmake_parse_arguments(US_TEST "SKIP_MODULE_LIST" "RESOURCES_ROOT" "SOURCES;RESOURCES;LINK_LIBRARIES" "" ${ARGN}) + set(_srcs ${US_TEST_SOURCES} ${name}_resources.cpp) set(_res_files ${US_TEST_RESOURCES}) if(US_TEST_RESOURCES_ROOT) set(_res_root ${US_TEST_RESOURCES_ROOT}) else() - set(_res_root resources) + set(_res_root ${CMAKE_CURRENT_SOURCE_DIR}/resources) endif() - usFunctionGenerateModuleInit(_srcs NAME "${name} Module" LIBRARY_NAME ${name}) + usFunctionGenerateModuleInit(_srcs) _us_create_test_module_helper() endfunction() diff --git a/Core/CppMicroServices/cmake/usFunctionEmbedResources.cmake b/Core/CppMicroServices/cmake/usFunctionEmbedResources.cmake deleted file mode 100644 index ab81fc293e..0000000000 --- a/Core/CppMicroServices/cmake/usFunctionEmbedResources.cmake +++ /dev/null @@ -1,155 +0,0 @@ -#! \ingroup MicroServicesCMake -#! \brief Embed resources into a shared library or executable. -#! -#! This CMake function uses an external command line program to generate a source -#! file containing data from external resources such as text files or images. The path -#! to the generated source file is appended to the \c src_var variable. -#! -#! Each module can call this function (at most once) to embed resources and make them -#! available at runtime through the Module class. Resources can also be embedded into -#! executables, using the EXECUTABLE_NAME argument instead of LIBRARY_NAME. -#! -#! Example usage: -#! \code{.cmake} -#! set(module_srcs ) -#! usFunctionEmbedResources(module_srcs -#! LIBRARY_NAME "mylib" -#! ROOT_DIR resources -#! FILES config.properties logo.png -#! ) -#! \endcode -#! -#! \param LIBRARY_NAME (required if EXECUTABLE_NAME is empty) The library name of the module -#! which will include the generated source file, without extension. -#! \param EXECUTABLE_NAME (required if LIBRARY_NAME is empty) The name of the executable -#! which will include the generated source file. -#! \param COMPRESSION_LEVEL (optional) The zip compression level. Defaults to the default zip -#! level. Level 0 disables compression. -#! \param COMPRESSION_THRESHOLD (optional) The compression threshold ranging from 0 to 100 for -#! actually compressing the resource data. The default threshold is 30, meaning a size -#! reduction of 30 percent or better results in the resource data being compressed. -#! \param ROOT_DIR (optional) The root path for all resources listed after the FILES argument. -#! If no or a relative path is given, it is considered relativ to the current CMake source directory. -#! \param FILES (optional) A list of resources (paths to external files in the file system) relative -#! to the ROOT_DIR argument or the current CMake source directory if ROOT_DIR is empty. -#! -#! The ROOT_DIR and FILES arguments may be repeated any number of times to merge files from -#! different root directories into the embedded resource tree (hence the relative file paths -#! after the FILES argument must be unique). -#! -function(usFunctionEmbedResources src_var) - - set(prefix US_RESOURCE) - set(arg_names LIBRARY_NAME EXECUTABLE_NAME COMPRESSION_LEVEL COMPRESSION_THRESHOLD ROOT_DIR FILES) - foreach(arg_name ${arg_names}) - set(${prefix}_${arg_name}) - endforeach(arg_name) - - set(cmd_line_args ) - set(absolute_res_files ) - set(current_arg_name DEFAULT_ARGS) - set(current_arg_list) - set(current_root_dir ${CMAKE_CURRENT_SOURCE_DIR}) - foreach(arg ${ARGN}) - - list(FIND arg_names "${arg}" is_arg_name) - - if(is_arg_name GREATER -1) - set(${prefix}_${current_arg_name} ${current_arg_list}) - set(current_arg_name "${arg}") - set(current_arg_list) - else() - set(current_arg_list ${current_arg_list} "${arg}") - if(current_arg_name STREQUAL "ROOT_DIR") - set(current_root_dir "${arg}") - if(NOT IS_ABSOLUTE ${current_root_dir}) - set(current_root_dir "${CMAKE_CURRENT_SOURCE_DIR}/${current_root_dir}") - endif() - if(NOT IS_DIRECTORY ${current_root_dir}) - message(SEND_ERROR "The ROOT_DIR argument is not a directory: ${current_root_dir}") - endif() - get_filename_component(current_root_dir "${current_root_dir}" REALPATH) - if(WIN32) - string(REPLACE "/" "\\" current_root_dir "${current_root_dir}") - endif() - list(APPEND cmd_line_args -d "${current_root_dir}") - elseif(current_arg_name STREQUAL "FILES") - set(res_file "${current_root_dir}/${arg}") - if(WIN32) - string(REPLACE "/" "\\" res_file_native "${res_file}") - else() - set(res_file_native "${res_file}") - endif() - if(IS_DIRECTORY ${res_file}) - message(SEND_ERROR "A resource cannot be a directory: ${res_file_native}") - endif() - if(NOT EXISTS ${res_file}) - message(SEND_ERROR "Resource does not exists: ${res_file_native}") - endif() - list(APPEND absolute_res_files ${res_file}) - file(TO_NATIVE_PATH "${arg}" res_filename_native) - list(APPEND cmd_line_args "${res_filename_native}") - endif() - endif(is_arg_name GREATER -1) - - endforeach(arg ${ARGN}) - - set(${prefix}_${current_arg_name} ${current_arg_list}) - - if(NOT src_var) - message(SEND_ERROR "Output variable name not specified.") - endif() - - if(US_RESOURCE_EXECUTABLE_NAME AND US_RESOURCE_LIBRARY_NAME) - message(SEND_ERROR "Only one of LIBRARY_NAME or EXECUTABLE_NAME can be specified.") - endif() - - if(NOT US_RESOURCE_LIBRARY_NAME AND NOT US_RESOURCE_EXECUTABLE_NAME) - message(SEND_ERROR "LIBRARY_NAME or EXECUTABLE_NAME argument not specified.") - endif() - - if(NOT US_RESOURCE_FILES) - message(WARNING "No FILES argument given. Skipping resource processing.") - return() - endif() - - list(GET cmd_line_args 0 first_arg) - if(NOT first_arg STREQUAL "-d") - set(cmd_line_args -d "${CMAKE_CURRENT_SOURCE_DIR}" ${cmd_line_args}) - endif() - - if(US_RESOURCE_COMPRESSION_LEVEL) - set(cmd_line_args -c ${US_RESOURCE_COMPRESSION_LEVEL} ${cmd_line_args}) - endif() - - if(US_RESOURCE_COMPRESSION_THRESHOLD) - set(cmd_line_args -t ${US_RESOURCE_COMPRESSION_THRESHOLD} ${cmd_line_args}) - endif() - - if(US_RESOURCE_LIBRARY_NAME) - set(us_cpp_resource_file "${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_LIBRARY_NAME}_resources.cpp") - set(us_lib_name ${US_RESOURCE_LIBRARY_NAME}) - else() - set(us_cpp_resource_file "${CMAKE_CURRENT_BINARY_DIR}/${US_RESOURCE_EXECUTABLE_NAME}_resources.cpp") - set(us_lib_name "") - endif() - - set(resource_compiler ${US_RCC_EXECUTABLE}) - if(TARGET ${US_RCC_EXECUTABLE_NAME}) - set(resource_compiler ${US_RCC_EXECUTABLE_NAME}) - elseif(NOT resource_compiler) - message(FATAL_ERROR "The CppMicroServices resource compiler was not found. Check the US_RCC_EXECUTABLE CMake variable.") - endif() - - add_custom_command( - OUTPUT ${us_cpp_resource_file} - COMMAND ${resource_compiler} "${us_lib_name}" ${us_cpp_resource_file} ${cmd_line_args} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${absolute_res_files} ${resource_compiler} - COMMENT "Generating embedded resource file ${us_cpp_resource_name}" - VERBATIM - ) - - set(${src_var} "${${src_var}};${us_cpp_resource_file}" PARENT_SCOPE) - -endfunction() diff --git a/Core/CppMicroServices/cmake/usFunctionGenerateExecutableInit.cmake b/Core/CppMicroServices/cmake/usFunctionGenerateExecutableInit.cmake deleted file mode 100644 index 6fb6ad80dc..0000000000 --- a/Core/CppMicroServices/cmake/usFunctionGenerateExecutableInit.cmake +++ /dev/null @@ -1,43 +0,0 @@ -#! \ingroup MicroServicesCMake -#! \brief Generate a source file which handles proper initialization of an executable. -#! -#! This CMake function will store the path to a generated source file in the -#! src_var variable, which should be compiled into an executable. Example usage: -#! -#! \code{.cmake} -#! set(executable_srcs ) -#! usFunctionGenerateExecutableInit(executable_srcs -#! IDENTIFIER "MyExecutable" -#! ) -#! add_executable(MyExecutable ${executable_srcs}) -#! \endcode -#! -#! \param src_var (required) The name of a list variable to which the path of the generated -#! source file will be appended. -#! \param IDENTIFIER (required) A valid C identifier for the executable. -#! -#! \see #usFunctionGenerateModuleInit -#! \see \ref MicroServices_AutoLoading -#! -function(usFunctionGenerateExecutableInit src_var) - - cmake_parse_arguments(US_EXECUTABLE "" "IDENTIFIER" "" ${ARGN}) - - # sanity checks - if(NOT US_EXECUTABLE_IDENTIFIER) - message(SEND_ERROR "IDENTIFIER argument is mandatory") - endif() - - set(_regex_validation "[a-zA-Z_-][a-zA-Z_0-9-]*") - string(REGEX MATCH ${_regex_validation} _valid_chars ${US_EXECUTABLE_IDENTIFIER}) - if(NOT _valid_chars STREQUAL US_EXECUTABLE_IDENTIFIER) - message(FATAL_ERROR "IDENTIFIER contains illegal characters.") - endif() - - set(exec_init_src_file "${CMAKE_CURRENT_BINARY_DIR}/${US_EXECUTABLE_IDENTIFIER}_init.cpp") - configure_file(${US_EXECUTABLE_INIT_TEMPLATE} ${exec_init_src_file} @ONLY) - - set(_src ${exec_init_src_file} ${${src_var}}) - set(${src_var} ${_src} PARENT_SCOPE) - -endfunction() diff --git a/Core/CppMicroServices/cmake/usFunctionGenerateModuleInit.cmake b/Core/CppMicroServices/cmake/usFunctionGenerateModuleInit.cmake index 6986d228b8..b8d38faf7b 100644 --- a/Core/CppMicroServices/cmake/usFunctionGenerateModuleInit.cmake +++ b/Core/CppMicroServices/cmake/usFunctionGenerateModuleInit.cmake @@ -1,54 +1,29 @@ #! \ingroup MicroServicesCMake #! \brief Generate a source file which handles proper initialization of a module. #! #! This CMake function will store the path to a generated source file in the -#! src_var variable, which should be compiled into a module. Example usage: +#! src_var variable, which should be compiled into a module. The modules source +#! code must be compiled with the US_MODULE_NAME pre-processor definition. +#! Example usage: #! #! \code{.cmake} #! set(module_srcs ) -#! usFunctionGenerateModuleInit(module_srcs -#! NAME "My Module" -#! LIBRARY_NAME "mylib" -#! ) +#! usFunctionGenerateModuleInit(module_srcs) #! add_library(mylib ${module_srcs}) +#! set_property(TARGET ${mylib} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=MyModule) #! \endcode #! #! \param src_var (required) The name of a list variable to which the path of the generated #! source file will be appended. -#! \param NAME (required) A human-readable name for the module. -#! \param LIBRARY_NAME (optional) The name of the module, without extension. If empty, the -#! NAME argument will be used. #! -#! \see #usFunctionGenerateExecutableInit #! \see \ref MicroServices_AutoLoading #! function(usFunctionGenerateModuleInit src_var) - cmake_parse_arguments(US_MODULE "EXECUTABLE" "NAME;LIBRARY_NAME" "" ${ARGN}) - - if(US_MODULE_EXECUTABLE) - message(SEND_ERROR "EXECUTABLE option no longer supported. Use usFunctionGenerateExecutableInit instead.") - endif() - - # sanity checks - if(NOT US_MODULE_NAME) - message(SEND_ERROR "NAME argument is mandatory") - endif() - - if(NOT US_MODULE_LIBRARY_NAME) - set(US_MODULE_LIBRARY_NAME ${US_MODULE_NAME}) - endif() - - set(_regex_validation "[a-zA-Z_-][a-zA-Z_0-9-]*") - string(REGEX MATCH ${_regex_validation} _valid_chars ${US_MODULE_LIBRARY_NAME}) - if(NOT _valid_chars STREQUAL US_MODULE_LIBRARY_NAME) - message(FATAL_ERROR "[Module: ${US_MODULE_NAME}] LIBRARY_NAME \"${US_MODULE_LIBRARY_NAME}\" contains illegal characters.") - endif() - - set(module_init_src_file "${CMAKE_CURRENT_BINARY_DIR}/${US_MODULE_LIBRARY_NAME}_init.cpp") + set(module_init_src_file "${CMAKE_CURRENT_BINARY_DIR}/us_init.cpp") configure_file(${US_MODULE_INIT_TEMPLATE} ${module_init_src_file} @ONLY) set(_src ${module_init_src_file} ${${src_var}}) set(${src_var} ${_src} PARENT_SCOPE) endfunction() diff --git a/Core/CppMicroServices/cmake/usGlobalConfig.h.in b/Core/CppMicroServices/cmake/usGlobalConfig.h.in index 1ee63faa75..441e3eae5b 100644 --- a/Core/CppMicroServices/cmake/usGlobalConfig.h.in +++ b/Core/CppMicroServices/cmake/usGlobalConfig.h.in @@ -1,206 +1,206 @@ /* USCONFIG.h this file is generated. Do not change! */ #ifndef USGLOBALCONFIG_H #define USGLOBALCONFIG_H #cmakedefine US_BUILD_SHARED_LIBS #cmakedefine US_ENABLE_THREADING_SUPPORT -#cmakedefine US_ENABLE_RESOURCE_COMPRESSION #cmakedefine US_GCC_RTTI_WORKAROUND_NEEDED #cmakedefine US_HAVE_VISIBILITY_ATTRIBUTE //------------------------------------------------------------------- // Header Availability //------------------------------------------------------------------- +#cmakedefine US_HAVE_CXXABI_H #cmakedefine US_HAVE_STDINT_H #cmakedefine US_HAVE_TR1_UNORDERED_MAP_H #cmakedefine US_HAVE_TR1_UNORDERED_SET_H #cmakedefine US_HAVE_TR1_FUNCTIONAL_H #cmakedefine US_HAVE_UNORDERED_MAP_H #cmakedefine US_HAVE_UNORDERED_SET_H #cmakedefine US_HAVE_FUNCTIONAL_H #cmakedefine US_HAVE_TR1_UNORDERED_MAP #cmakedefine US_HAVE_TR1_UNORDERED_SET #cmakedefine US_HAVE_TR1_FUNCTION #cmakedefine US_HAVE_STD_UNORDERED_MAP #cmakedefine US_HAVE_STD_UNORDERED_SET #cmakedefine US_HAVE_STD_FUNCTION #cmakedefine US_HAVE_TR1_HASH #cmakedefine US_HAVE_TR1_HASH_STRUCT #cmakedefine US_HAVE_TR1_HASH_CLASS #cmakedefine US_HAVE_STD_HASH #cmakedefine US_HAVE_STD_HASH_STRUCT #cmakedefine US_HAVE_STD_HASH_CLASS ///------------------------------------------------------------------- // Version information //------------------------------------------------------------------- #define CppMicroServices_MAJOR_VERSION @CppMicroServices_MAJOR_VERSION@ #define CppMicroServices_MINOR_VERSION @CppMicroServices_MINOR_VERSION@ #define CppMicroServices_PATCH_VERSION @CppMicroServices_PATCH_VERSION@ #define CppMicroServices_VERSION @CppMicroServices_VERSION@ #define CppMicroServices_VERSION_STR "@CppMicroServices_VERSION@" #define US_MAJOR_VERSION @CppMicroServices_MAJOR_VERSION@ #define US_MINOR_VERSION @CppMicroServices_MINOR_VERSION@ #define US_PATCH_VERSION @CppMicroServices_PATCH_VERSION@ #define US_VERSION @CppMicroServices_VERSION@ #define US_VERSION_STR "@CppMicroServices_VERSION@" //------------------------------------------------------------------- // Namespace customization //------------------------------------------------------------------- #define US_NAMESPACE @US_NAMESPACE@ #ifndef US_NAMESPACE /* user namespace */ # define US_PREPEND_NAMESPACE(name) ::name # define US_USE_NAMESPACE # define US_BEGIN_NAMESPACE # define US_END_NAMESPACE # define US_FORWARD_DECLARE_CLASS(name) class name; # define US_FORWARD_DECLARE_STRUCT(name) struct name; #else /* user namespace */ # define US_PREPEND_NAMESPACE(name) ::US_NAMESPACE::name # define US_USE_NAMESPACE using namespace ::US_NAMESPACE; # define US_BEGIN_NAMESPACE namespace US_NAMESPACE { # define US_END_NAMESPACE } # define US_FORWARD_DECLARE_CLASS(name) \ US_BEGIN_NAMESPACE class name; US_END_NAMESPACE # define US_FORWARD_DECLARE_STRUCT(name) \ US_BEGIN_NAMESPACE struct name; US_END_NAMESPACE namespace US_NAMESPACE {} #endif /* user namespace */ //------------------------------------------------------------------- // Platform defines //------------------------------------------------------------------- #if defined(__APPLE__) #define US_PLATFORM_APPLE #endif #if defined(__linux__) #define US_PLATFORM_LINUX #endif #if defined(_WIN32) || defined(_WIN64) #define US_PLATFORM_WINDOWS #else #define US_PLATFORM_POSIX #endif ///------------------------------------------------------------------- // Macros for import/export declarations //------------------------------------------------------------------- #if defined(US_PLATFORM_WINDOWS) #define US_ABI_EXPORT __declspec(dllexport) #define US_ABI_IMPORT __declspec(dllimport) #define US_ABI_LOCAL #elif defined(US_HAVE_VISIBILITY_ATTRIBUTE) #define US_ABI_EXPORT __attribute__ ((visibility ("default"))) #define US_ABI_IMPORT __attribute__ ((visibility ("default"))) #define US_ABI_LOCAL __attribute__ ((visibility ("hidden"))) #else #define US_ABI_EXPORT #define US_ABI_IMPORT #define US_ABI_LOCAL #endif //------------------------------------------------------------------- // Macros for suppressing warnings //------------------------------------------------------------------- #ifdef _MSC_VER #define US_MSVC_PUSH_DISABLE_WARNING(wn) \ __pragma(warning(push)) \ __pragma(warning(disable:wn)) #define US_MSVC_POP_WARNING \ __pragma(warning(pop)) #define US_MSVC_DISABLE_WARNING(wn) \ __pragma(warning(disable:wn)) #else #define US_MSVC_PUSH_DISABLE_WARNING(wn) #define US_MSVC_POP_WARNING #define US_MSVC_DISABLE_WARNING(wn) #endif // Do not warn about the usage of deprecated unsafe functions US_MSVC_DISABLE_WARNING(4996) // Mark a variable or expression result as unused #define US_UNUSED(x) (void)(x) //------------------------------------------------------------------- // Hash Container //------------------------------------------------------------------- #ifdef US_HAVE_UNORDERED_MAP_H #include #elif defined(US_HAVE_TR1_UNORDERED_MAP_H) #include #endif #ifdef US_HAVE_UNORDERED_SET_H #include #elif defined(US_HAVE_TR1_UNORDERED_SET_H) #include #endif #ifdef US_HAVE_STD_UNORDERED_MAP #define US_UNORDERED_MAP_TYPE ::std::unordered_map #elif defined(US_HAVE_TR1_UNORDERED_MAP) #define US_UNORDERED_MAP_TYPE ::std::tr1::unordered_map #endif #ifdef US_HAVE_STD_UNORDERED_SET #define US_UNORDERED_SET_TYPE ::std::unordered_set #elif defined(US_HAVE_TR1_UNORDERED_SET) #define US_UNORDERED_SET_TYPE ::std::tr1::unordered_set #endif #ifdef US_HAVE_STD_HASH #define US_HASH_FUNCTION_NAMESPACE ::std #ifdef US_HAVE_STD_HASH_STRUCT #define US_HASH_FUNCTION_FRIEND(type) friend struct ::std::hash #elif defined(US_HAVE_STD_HASH_CLASS) #define US_HASH_FUNCTION_FRIEND(type) friend class ::std::hash #endif #define US_HASH_FUNCTION_NAMESPACE_BEGIN namespace std { #define US_HASH_FUNCTION_NAMESPACE_END } #elif defined(US_HAVE_TR1_HASH) #define US_HASH_FUNCTION_NAMESPACE ::std::tr1 #ifdef US_HAVE_TR1_HASH_STRUCT #define US_HASH_FUNCTION_FRIEND(type) friend struct ::std::tr1::hash #elif defined(US_HAVE_TR1_HASH_CLASS) #define US_HASH_FUNCTION_FRIEND(type) friend class ::std::tr1::hash #endif #define US_HASH_FUNCTION_NAMESPACE_BEGIN namespace std { namespace tr1 { #define US_HASH_FUNCTION_NAMESPACE_END }} #endif #define US_HASH_FUNCTION_BEGIN(type) \ template<> \ struct hash : std::unary_function { \ std::size_t operator()(const type& arg) const { #define US_HASH_FUNCTION_END } }; #define US_HASH_FUNCTION(type, arg) hash()(arg) #endif // USGLOBALCONFIG_H diff --git a/Core/CppMicroServices/cmake/usMacroCreateModule.cmake b/Core/CppMicroServices/cmake/usMacroCreateModule.cmake new file mode 100644 index 0000000000..6f7a451773 --- /dev/null +++ b/Core/CppMicroServices/cmake/usMacroCreateModule.cmake @@ -0,0 +1,220 @@ +# For internal use only + +macro(usMacroCreateModule _project_name) + +project(${_project_name}) + +cmake_parse_arguments(${PROJECT_NAME} + "SKIP_EXAMPLES;SKIP_INIT" + "VERSION;TARGET" + "DEPENDS;INTERNAL_INCLUDE_DIRS;LINK_LIBRARIES;SOURCES;PRIVATE_HEADERS;PUBLIC_HEADERS;RESOURCES;BINARY_RESOURCES" + ${ARGN} +) + +if(NOT ${PROJECT_NAME}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+$") + message(SEND_ERROR "VERSION argument invalid: ${${PROJECT_NAME}_VERSION}") +endif() + +string(REPLACE "." ";" _version_numbers ${${PROJECT_NAME}_VERSION}) +list(GET _version_numbers 0 ${PROJECT_NAME}_MAJOR_VERSION) +list(GET _version_numbers 1 ${PROJECT_NAME}_MINOR_VERSION) +list(GET _version_numbers 2 ${PROJECT_NAME}_PATCH_VERSION) + +if(NOT ${PROJECT_NAME}_TARGET) + set(${PROJECT_NAME}_TARGET us${PROJECT_NAME}) +endif() +set(PROJECT_TARGET ${${PROJECT_NAME}_TARGET}) + +if(${PROJECT_NAME}_DEPENDS) + find_package(CppMicroServices REQUIRED ${${PROJECT_NAME}_DEPENDS} QUIET + HINTS ${CppMicroServices_BINARY_DIR} + NO_DEFAULT_PATH + ) +endif() + +#----------------------------------------------------------------------------- +# Include dirs and libraries +#----------------------------------------------------------------------------- + +set(${PROJECT_NAME}_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include +) + +configure_file(${CppMicroServices_SOURCE_DIR}/cmake/usExport.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Export.h) +list(APPEND ${PROJECT_NAME}_PUBLIC_HEADERS + ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Export.h) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include/us${PROJECT_NAME}Config.h.in) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/us${PROJECT_NAME}Config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Config.h) + list(APPEND ${PROJECT_NAME}_PUBLIC_HEADERS + ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Config.h) +endif() + +include_directories( + ${US_INCLUDE_DIRS} + ${${PROJECT_NAME}_INCLUDE_DIRS} +) + +set(_internal_include_dirs ${${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS}) +set(${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS ) +if(_internal_include_dirs) + foreach(_internal_include_dir ${_internal_include_dirs}) + if(IS_ABSOLUTE "${_internal_include_dir}") + list(APPEND ${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS ${_internal_include_dir}) + else() + list(APPEND ${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/${_internal_include_dir}) + endif() + endforeach() + include_directories(${${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS}) +endif() + +#----------------------------------------------------------------------------- +# Create library +#----------------------------------------------------------------------------- + +# Generate the module init file +if(NOT ${PROJECT_NAME}_SKIP_INIT) + usFunctionGenerateModuleInit(${PROJECT_NAME}_SOURCES) +endif() + +if(${PROJECT_NAME}_RESOURCES OR ${PROJECT_NAME}_BINARY_RESOURCES) + list(APPEND ${PROJECT_NAME}_SOURCES ${${PROJECT_NAME}_TARGET}_resources.cpp) +endif() + +# Create the module library +add_library(${${PROJECT_NAME}_TARGET} ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_PRIVATE_HEADERS} ${${PROJECT_NAME}_PUBLIC_HEADERS}) + +# Compile definitions +set_property(TARGET ${${PROJECT_NAME}_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${${PROJECT_NAME}_TARGET}) +set_property(TARGET ${${PROJECT_NAME}_TARGET} PROPERTY US_MODULE_NAME ${${PROJECT_NAME}_TARGET}) +if(NOT US_BUILD_SHARED_LIBS) + set_property(TARGET ${${PROJECT_NAME}_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE) +endif() + +# Link flags +if(${PROJECT_NAME}_LINK_FLAGS OR US_LINK_FLAGS) + set_target_properties(${${PROJECT_NAME}_TARGET} PROPERTIES + LINK_FLAGS "${US_LINK_FLAGS} ${${PROJECT_NAME}_LINK_FLAGS}" + ) +endif() + +set_target_properties(${${PROJECT_NAME}_TARGET} PROPERTIES + SOVERSION ${${PROJECT_NAME}_VERSION} + PUBLIC_HEADER "${${PROJECT_NAME}_PUBLIC_HEADERS}" + PRIVATE_HEADER "${${PROJECT_NAME}_PRIVATE_HEADERS}" +) + +# Link additional libraries +if(${PROJECT_NAME}_LINK_LIBRARIES OR US_LIBRARIES) + target_link_libraries(${${PROJECT_NAME}_TARGET} ${US_LIBRARIES} ${${PROJECT_NAME}_LINK_LIBRARIES}) +endif() + +# Embed module resources + +if(${PROJECT_NAME}_RESOURCES OR US_LIBRARIES) + usFunctionAddResources(TARGET ${${PROJECT_NAME}_TARGET} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/resources + FILES ${${PROJECT_NAME}_RESOURCES} + ZIP_ARCHIVES ${US_LIBRARIES} + ) +endif() +if(${PROJECT_NAME}_BINARY_RESOURCES) + usFunctionAddResources(TARGET ${${PROJECT_NAME}_TARGET} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/resources + FILES ${${PROJECT_NAME}_BINARY_RESOURCES} + ) +endif() + +#----------------------------------------------------------------------------- +# Install support +#----------------------------------------------------------------------------- + +install(TARGETS ${${PROJECT_NAME}_TARGET} + EXPORT us${PROJECT_NAME}Targets + RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} + LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} + ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} + PUBLIC_HEADER DESTINATION ${HEADER_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} + PRIVATE_HEADER DESTINATION ${HEADER_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT}) + +#----------------------------------------------------------------------------- +# US testing +#----------------------------------------------------------------------------- + +if(US_BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt") + add_subdirectory(test) +endif() + +#----------------------------------------------------------------------------- +# Documentation +#----------------------------------------------------------------------------- + +if(US_BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/doc/snippets/CMakeLists.txt") + # Compile source code snippets + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/doc/snippets) +endif() + +#----------------------------------------------------------------------------- +# Last configuration and install steps +#----------------------------------------------------------------------------- + +export(TARGETS ${${PROJECT_NAME}_TARGET} ${US_LIBRARIES} + FILE ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}Targets.cmake) +install(EXPORT us${PROJECT_NAME}Targets + FILE us${PROJECT_NAME}Targets.cmake + DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR}) + +# Configure config file for the build tree + +set(PACKAGE_CONFIG_INCLUDE_DIR + ${${PROJECT_NAME}_INCLUDE_DIRS} + ${${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS}) +set(PACKAGE_CONFIG_RUNTIME_LIBRARY_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + +configure_file( + ${US_CMAKE_DIR}/usModuleConfig.cmake.in + ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}Config.cmake + @ONLY + ) + +# Configure config file for the install tree + +set(CONFIG_INCLUDE_DIR ${HEADER_INSTALL_DIR}) +set(CONFIG_RUNTIME_LIBRARY_DIR ${RUNTIME_INSTALL_DIR}) + +configure_package_config_file( + ${US_CMAKE_DIR}/usModuleConfig.cmake.in + ${CppMicroServices_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/us${PROJECT_NAME}Config.cmake + INSTALL_DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR} + PATH_VARS CONFIG_INCLUDE_DIR CONFIG_RUNTIME_LIBRARY_DIR + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + +# Version information +configure_file( + ${US_CMAKE_DIR}/usModuleConfigVersion.cmake.in + ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}ConfigVersion.cmake + @ONLY + ) + +install(FILES ${CppMicroServices_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/us${PROJECT_NAME}Config.cmake + ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}ConfigVersion.cmake + DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR} + ${US_SDK_INSTALL_COMPONENT}) + +#----------------------------------------------------------------------------- +# Build the examples +#----------------------------------------------------------------------------- + +if(US_BUILD_EXAMPLES AND NOT ${PROJECT_NAME}_SKIP_EXAMPLES AND + EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt) + set(CppMicroServices_DIR ${CppMicroServices_BINARY_DIR}) + add_subdirectory(examples) +endif() + +endmacro() diff --git a/Core/CppMicroServices/core/usCoreConfig.cmake.in b/Core/CppMicroServices/cmake/usModuleConfig.cmake.in similarity index 100% rename from Core/CppMicroServices/core/usCoreConfig.cmake.in rename to Core/CppMicroServices/cmake/usModuleConfig.cmake.in diff --git a/Core/CppMicroServices/core/usCoreConfigVersion.cmake.in b/Core/CppMicroServices/cmake/usModuleConfigVersion.cmake.in similarity index 100% rename from Core/CppMicroServices/core/usCoreConfigVersion.cmake.in rename to Core/CppMicroServices/cmake/usModuleConfigVersion.cmake.in diff --git a/Core/CppMicroServices/cmake/usModuleInit.cpp b/Core/CppMicroServices/cmake/usModuleInit.cpp index 05678e8649..13cc7500d0 100644 --- a/Core/CppMicroServices/cmake/usModuleInit.cpp +++ b/Core/CppMicroServices/cmake/usModuleInit.cpp @@ -1,24 +1,24 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include -US_INITIALIZE_MODULE("@US_MODULE_NAME@", "@US_MODULE_LIBRARY_NAME@") +US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/CMakeLists.txt b/Core/CppMicroServices/core/CMakeLists.txt index 466d699cfd..244fa9a635 100644 --- a/Core/CppMicroServices/core/CMakeLists.txt +++ b/Core/CppMicroServices/core/CMakeLists.txt @@ -1,202 +1,45 @@ -project(Core) - -set(${PROJECT_NAME}_MAJOR_VERSION 2) -set(${PROJECT_NAME}_MINOR_VERSION 99) -set(${PROJECT_NAME}_PATCH_VERSION 0) -set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) - -set(${PROJECT_NAME}_TARGET CppMicroServices) -set(PROJECT_TARGET ${${PROJECT_NAME}_TARGET}) - -cmake_minimum_required(VERSION 2.8) -cmake_policy(VERSION 2.8) -cmake_policy(SET CMP0017 NEW) - -#----------------------------------------------------------------------------- -# US include dirs and libraries -#----------------------------------------------------------------------------- - -set(${PROJECT_NAME}_INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR}/include -) - -set(${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/src/util - ${CMAKE_CURRENT_SOURCE_DIR}/src/service - ${CMAKE_CURRENT_SOURCE_DIR}/src/module -) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/us${PROJECT_NAME}Config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Config.h -) - -configure_file(${CppMicroServices_SOURCE_DIR}/cmake/usExport.h.in - ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Export.h -) - -include_directories( - ${${PROJECT_NAME}_INCLUDE_DIRS} - ${${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS} -) - -# link libraries for the CppMicroServices lib -set(${PROJECT_NAME}_LINK_LIBRARIES ) -if(UNIX) - list(APPEND ${PROJECT_NAME}_LINK_LIBRARIES dl) -endif() - -#----------------------------------------------------------------------------- -# Create library -#----------------------------------------------------------------------------- - +# sources and headers include(${CMAKE_CURRENT_SOURCE_DIR}/src/CMakeLists.txt) include(${CMAKE_CURRENT_SOURCE_DIR}/include/CMakeLists.txt) -set(${PROJECT_NAME}_SOURCES ) +set(_core_srcs ) foreach(_src ${_srcs}) - list(APPEND ${PROJECT_NAME}_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/${_src}) + list(APPEND _core_srcs ${CMAKE_CURRENT_SOURCE_DIR}/src/${_src}) endforeach() -set(${PROJECT_NAME}_PRIVATE_HEADERS ) +set(_core_private_headers) foreach(_header ${_private_headers}) - list(APPEND ${PROJECT_NAME}_PRIVATE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/${_header}) + list(APPEND _core_private_headers ${CMAKE_CURRENT_SOURCE_DIR}/src/${_header}) endforeach() -set(${PROJECT_NAME}_PUBLIC_HEADERS ) +set(_core_public_headers ) foreach(_header ${_public_headers}) - list(APPEND ${PROJECT_NAME}_PUBLIC_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/${_header}) + list(APPEND _core_public_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/${_header}) endforeach() -list(APPEND ${PROJECT_NAME}_PUBLIC_HEADERS - ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Config.h - ${CMAKE_CURRENT_BINARY_DIR}/include/us${PROJECT_NAME}Export.h -) -# Generate the module init file -usFunctionGenerateModuleInit(${PROJECT_NAME}_SOURCES NAME ${${PROJECT_NAME}_TARGET}) +# link libraries for the CppMicroServices lib +set(_link_libraries ) +if(UNIX) + list(APPEND _link_libraries dl) +endif() # Configure the modules manifest.json file configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resources/manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/resources/manifest.json) -# Embed module resources -usFunctionEmbedResources(${PROJECT_NAME}_SOURCES LIBRARY_NAME ${${PROJECT_NAME}_TARGET} - ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/resources - FILES manifest.json) - -# Create the module library -add_library(${${PROJECT_NAME}_TARGET} ${${PROJECT_NAME}_SOURCES} - ${${PROJECT_NAME}_PRIVATE_HEADERS} ${${PROJECT_NAME}_PUBLIC_HEADERS} ${us_config_h_file}) - -# Link flags -if(${PROJECT_NAME}_LINK_FLAGS OR US_LINK_FLAGS) - set_target_properties(${${PROJECT_NAME}_TARGET} PROPERTIES - LINK_FLAGS "${US_LINK_FLAGS} ${${PROJECT_NAME}_LINK_FLAGS}" - ) -endif() - -# Compile definitions -set_property(TARGET ${${PROJECT_NAME}_TARGET} - APPEND PROPERTY COMPILE_DEFINITIONS US_FORCE_MODULE_INIT -) - -set_target_properties(${${PROJECT_NAME}_TARGET} PROPERTIES - SOVERSION ${${PROJECT_NAME}_VERSION} - PUBLIC_HEADER "${${PROJECT_NAME}_PUBLIC_HEADERS}" - PRIVATE_HEADER "${${PROJECT_NAME}_PRIVATE_HEADERS}" -) - -# Link additional libraries -if(${PROJECT_NAME}_LINK_LIBRARIES) - target_link_libraries(${${PROJECT_NAME}_TARGET} ${${PROJECT_NAME}_LINK_LIBRARIES}) -endif() - - -#----------------------------------------------------------------------------- -# Install support -#----------------------------------------------------------------------------- - -install(TARGETS ${${PROJECT_NAME}_TARGET} - EXPORT us${PROJECT_NAME}Targets - RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} - LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} - ARCHIVE DESTINATION ${ARCHIVE_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} - PUBLIC_HEADER DESTINATION ${HEADER_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT} - PRIVATE_HEADER DESTINATION ${HEADER_INSTALL_DIR} ${US_SDK_INSTALL_COMPONENT}) - -#----------------------------------------------------------------------------- -# US testing -#----------------------------------------------------------------------------- - -if(US_BUILD_TESTING) - add_subdirectory(test) -endif() - -#----------------------------------------------------------------------------- -# Documentation -#----------------------------------------------------------------------------- - -add_subdirectory(doc) - -#----------------------------------------------------------------------------- -# Last configuration and install steps -#----------------------------------------------------------------------------- - -export(TARGETS ${${PROJECT_NAME}_TARGET} - FILE ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}Targets.cmake) -install(EXPORT us${PROJECT_NAME}Targets - FILE us${PROJECT_NAME}Targets.cmake - DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR}) - -# Configure CoreConfig.cmake for the build tree - -set(PACKAGE_CONFIG_INCLUDE_DIR - ${${PROJECT_NAME}_INCLUDE_DIRS} - ${${PROJECT_NAME}_INTERNAL_INCLUDE_DIRS}) -set(PACKAGE_CONFIG_RUNTIME_LIBRARY_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/us${PROJECT_NAME}Config.cmake.in - ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}Config.cmake - @ONLY - ) - -# Configure CoreConfig.cmake for the install tree - -set(CONFIG_INCLUDE_DIR ${HEADER_INSTALL_DIR}) -set(CONFIG_RUNTIME_LIBRARY_DIR ${RUNTIME_INSTALL_DIR}) - -configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/us${PROJECT_NAME}Config.cmake.in - ${CppMicroServices_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/us${PROJECT_NAME}Config.cmake - INSTALL_DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR} - PATH_VARS CONFIG_INCLUDE_DIR CONFIG_RUNTIME_LIBRARY_DIR - NO_SET_AND_CHECK_MACRO - NO_CHECK_REQUIRED_COMPONENTS_MACRO +usMacroCreateModule(Core + SKIP_INIT # we initialize the module in usModuleRegistry.cpp ourselves + VERSION "2.99.0" + TARGET CppMicroServices + INTERNAL_INCLUDE_DIRS src/util src/service src/module + LINK_LIBRARIES ${_link_libraries} + SOURCES ${_core_srcs} + PUBLIC_HEADERS ${_core_public_headers} + PRIVATE_HEADERS ${_core_private_headers} + BINARY_RESOURCES manifest.json +) + +set_property( + TARGET CppMicroServices APPEND PROPERTY + COMPILE_DEFINITIONS "MINIZ_NO_ARCHIVE_WRITING_API;MINIZ_NO_ZLIB_COMPATIBLE_NAMES" ) - -# Version information -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/us${PROJECT_NAME}ConfigVersion.cmake.in - ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}ConfigVersion.cmake - @ONLY - ) - -install(FILES ${CppMicroServices_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/us${PROJECT_NAME}Config.cmake - ${CppMicroServices_BINARY_DIR}/us${PROJECT_NAME}ConfigVersion.cmake - DESTINATION ${AUXILIARY_CMAKE_INSTALL_DIR} - ${US_SDK_INSTALL_COMPONENT} - ) - -#----------------------------------------------------------------------------- -# Build the examples -#----------------------------------------------------------------------------- - -if(US_BUILD_EXAMPLES) - if(NOT US_BUILD_SHARED_LIBS) - message(WARNING "Core examples are not available if US_BUILD_SHARED_LIBS is OFF") - else() - set(CppMicroServices_DIR ${CppMicroServices_BINARY_DIR}) - add_subdirectory(examples) - endif() -endif() diff --git a/Core/CppMicroServices/core/doc/doxygen/MicroServices_EmulateSingleton.dox b/Core/CppMicroServices/core/doc/doxygen/MicroServices_EmulateSingleton.dox index 55e51753e9..190535a9cb 100644 --- a/Core/CppMicroServices/core/doc/doxygen/MicroServices_EmulateSingleton.dox +++ b/Core/CppMicroServices/core/doc/doxygen/MicroServices_EmulateSingleton.dox @@ -1,89 +1,87 @@ /** \page MicroServices_EmulateSingleton Emulating singletons with micro services \section MicroServices_EmulateSingleton_1 Meyers Singleton Singletons are a well known pattern to ensure that only one instance of a class exists during the whole life-time of the application. A self-deleting variant is the "Meyers Singleton": \snippet uServices-singleton/SingletonOne.h s1 where the GetInstance() method is implemented as \snippet uServices-singleton/SingletonOne.cpp s1 If such a singleton is accessed during static deinitialization (which happens during unloading of shared libraries or application termination), your program might crash or even worse, exhibit undefined behavior, depending on your compiler and/or weekday. Such an access might happen in destructors of other objects with static life-time. For example, suppose that SingletonOne needs to call a second Meyers singleton during destruction: \snippet uServices-singleton/SingletonOne.cpp s1d If SingletonTwo was destroyed before SingletonOne, this leads to the mentioned problems. Note that this problem only occurs for static objects defined in the same shared library. Since you cannot reliably control the destruction order of global static objects, you must not introduce dependencies between them during static deinitialization. This is one reason why one should consider an alternative approach to singletons (unless you can absolutely make sure that nothing in your shared library will introduce such dependencies. Never.) Of course you could use something like a "Phoenix singleton" but that will have other drawbacks in certain scenarios. Returning pointers instead of references in GetInstance() would open up the possibility to return NULL, but than again this would not help if you require a non-NULL instance in your destructor. Another reason for an alternative approach is that singletons are usually not meant to be singletons for eternity. If your design evolves, you might hit a point where you suddenly need multiple instances of your singleton. \section MicroServices_EmulateSingleton_2 Singletons as a service The C++ Micro Services can be used to emulate the singleton pattern using a non-singleton class. This leaves room for future extensions without the need for heavy refactoring. Additionally, it gives you full control about the construction and destruction order of your "singletons" inside your shared library or executable, making it possible to have dependencies between them during destruction. \subsection MicroServices_EmulateSingleton_2_1 Converting a classic singleton We modify the previous SingletonOne class such that it internally uses the micro services API. The changes are discussed in detail below. \snippet uServices-singleton/SingletonOne.h ss1 - In the implementation above, the class SingletonOneService provides the implementation as well as the interface. - Friend activator: We move the responsibility of constructing instances of SingletonOneService from the GetInstance() method to the module activator. - - Service interface declaration: Because the SingletonOneService class introduces a new service interface, it must - be registered under a unique name using the helper macro US_DECLARE_SERVICE_INTERFACE. Let's have a look at the modified GetInstance() and ~SingletonOneService() methods. \snippet uServices-singleton/SingletonOne.cpp ss1gi The inline comments should explain the details. Note that we now had to change the return type to a pointer, instead of a reference as in the classic singleton. This is necessary since we can no longer guarantee that an instance always exists. Clients of the GetInstance() method must check for null pointers and react appropriately. \warning Newly created "singletons" should not expose a GetInstance() method. They should be handled as proper services and hence should be retrieved by clients using the ModuleContext or ServiceTracker API. The GetInstance() method is for migration purposes only. \snippet uServices-singleton/SingletonOne.cpp ss1d The SingletonTwoService::GetInstance() method is implemented exactly as in SingletonOneService. Because we know that the module activator guarantees that a SingletonTwoService instance will always be available during the life-time of a SingletonOneService instance (see below), we can assert a non-null pointer. Otherwise, we would have to handle the null-pointer case. The order of construction/registration and destruction/unregistration of our singletons (or any other services) is defined in the Load() and Unload() methods of the module activator. \snippet uServices-singleton/main.cpp 0 The Unload() method is defined as: \snippet uServices-singleton/main.cpp 1 */ diff --git a/Core/CppMicroServices/core/doc/doxygen/MicroServices_Resources.md b/Core/CppMicroServices/core/doc/doxygen/MicroServices_Resources.md index 101044d143..32e49f86f3 100644 --- a/Core/CppMicroServices/core/doc/doxygen/MicroServices_Resources.md +++ b/Core/CppMicroServices/core/doc/doxygen/MicroServices_Resources.md @@ -1,56 +1,67 @@ The Resources System {#MicroServices_Resources} ==================== The C++ Micro Services library provides a generic resources system to embed arbitrary files into a -module's shared library (the current size limitation is based on the largest source code file size -your compiler can handle). +module's shared library (the size limitation per resource is 2GB, due to the used ZIP format). The following features are supported: * Embed arbitrary data into shared or static modules or executables. - * Data is embedded in a compressed format if the size reduction exceeds a - configurable threshold. + * Data is embedded in a compressed format (zip) with a configurable compression level. * Resources are accessed via a Module instance, providing individual resource lookup and access for each module. * Resources are managed in a tree hierarchy, modeling the original child - parent relationship on the file-system. * The ModuleResource class provides a high-level API for accessing resource information and traversing the resource tree. * The ModuleResourceStream class provides an STL input stream derived class for the seamless usage of embedded resource data in third-party libraries. +The following conventions and limitations apply: + + * Resource entries are stored with case-insensitive names. On case-sensitive file systemes, + adding resources with the same name but different capitalization will lead to an error. + * Looking up resources by name at runtime *is* case sensitive. Embedding Resources in a %Module -------------------------------- -Resources are embedded by compiling a source file generated by the `usResourceCompiler` executable -into a module's shared or static library (or into an executable). +Resources are embedded into a module's shared or static library (or into an executable) +by using the `usResourceCompiler` executable. It will create a ZIP archive of all input +files and append it to the module file. -If you are using CMake, consider using the provided `#usFunctionEmbedResources` CMake macro which +If you are using CMake, consider using the provided `#usFunctionAddResources` CMake macro which handles the invocation of the `usResourceCompiler` executable and sets up the correct file -dependencies. +dependencies. Otherwise, you also need to make sure that the set of static modules linked +into a shared module or executable is also in the input file list of your `usResourceCompiler` +call for that shared module or executable. Accessing Resources at Runtime ------------------------------ Each module provides access to its embedded resources via the Module class which provides methods returning ModuleResource objects. The ModuleResourceStream class provides a std::istream compatible object to access the resource contents. The following example shows how to retrieve a resource from each currently loaded module whose path is specified by a module property: \snippet uServices-resources/main.cpp 2 This example could be enhanced to dynamically react to modules being loaded and unloaded, making use of the popular "extender pattern" from OSGi. -Limitations ------------ +Runtime overhead +---------------- + +The resources system has the following runtime characteristics: -Currently, the system has the following limitations: + * During static initialization of a module, it's ZIP archive header data (if available) + is parsed and stored in memory. + * Querying `Module` or `ModuleResource` objects for resource information will not + extract the embedded resource data and hence only has minimal runtime and memory + overhead. + * Creating a `ModuleResourceStream` object will allocate memory for the uncompressed + resource data and inflate it. The memory will be free'ed after the `ModuleResourceStream` + object is destroyed. - * At most one file generated by the `usResourceCompiler` executable can be compiled into a module's - shared library (you can work around this limitation by creating static modules and importing them). - * The size of embedded resources is limited by the file size your compiler can handle. However, the file - size is the sum of the size of all resources embedded into a module plus a small overhead. diff --git a/Core/CppMicroServices/core/doc/doxygen/MicroServices_StaticModules.md b/Core/CppMicroServices/core/doc/doxygen/MicroServices_StaticModules.md index 344cfb5146..c9ab730185 100644 --- a/Core/CppMicroServices/core/doc/doxygen/MicroServices_StaticModules.md +++ b/Core/CppMicroServices/core/doc/doxygen/MicroServices_StaticModules.md @@ -1,90 +1,66 @@ Static Modules {#MicroServices_StaticModules} ============== The normal and most flexible way to include a CppMicroServices module in an application is to compile it into a shared library that is either linked by another library (or executable) or \ref MicroServices_AutoLoading "auto-loaded" during runtime. However, modules can be linked statically to your application or shared library. This makes the deployment of your application less error-prone and in the case of a complete static build also minimizes its binary size and start-up time. The disadvantage is that no functionality can be added without a rebuild and redistribution of the application. # Creating Static Modules Static modules are written just like shared modules - there are no differences in the usage of the CppMicroServices API or the provided preprocessor macros. The only thing you need to make sure is that the `US_STATIC_MODULE` preprocessor macro is defined when building a module statically. # Using Static Modules Static modules can be used (imported) in shared or other static libraries or in the executable itself. -Assuming that a static module makes use of the CppMicroServices API (e.g. by registering some services -using a ModuleContext), the importing library or executable needs to put a call to the `#US_INITIALIZE_MODULE` -or the `#US_INITIALIZE_EXECUTABLE` macro somewhere in its source code. This ensures the availability of -a module context which is shared with all imported static libraries (see also \ref MicroServices_StaticModules_Context). - -\note Note that if your static module does not export a module activator by using the macro -`#US_EXPORT_MODULE_ACTIVATOR` or does not contain embedded resources (see \ref MicroServices_Resources) you -do not need to put the special import macros explained below into -your code. You can use and link the static module just like any other static library. - -For every static module you would like to import, you need to put a call to `#US_IMPORT_MODULE` into the -source code of the importing library. To make the static module's resources available to the importing module, -you must also call `#US_IMPORT_MODULE_RESOURCES`. Addidtionally, you need a call to `#US_LOAD_IMPORTED_MODULES` -which contains a space-deliminated list of module names in the importing libaries source code. This ensures -that the module activators of the imported static modules (if they exist) are called appropriately and that -the embedded resources are registered with the importing module. - -\note When importing a static module into another static module, the call to `#US_LOAD_IMPORTED_MODULES` in -the importing static module will have no effect. This macro can only be used in shared modules or executables. +For every static module you would like to import, you need to put a call to `#US_IMPORT_MODULE` or +to `#US_INITIALIZE_STATIC_MODULE` (if the module does not provide an activator) into the +source code of the importing library. + +\note While you can link static modules to other static modules, you will still need to +import *all* of the static modules into the final executable to ensure proper initialization. There are two main usage scenarios which are explained below together with some example code. ## Using a Shared CppMicroServices Library Building the CppMicroServices library as a shared library allows you to import static modules into other -shared or static modules or into the executable. As noted above, the importing shared module or executable -needs to provide a module context by calling the `#US_INITIALIZE_MODULE` or `#US_INITIALIZE_EXECUTABLE` macro. -Additionally, you must ensure to use the `#US_LOAD_IMPORTED_MODULES_INTO_MAIN` macro instead of -`#US_LOAD_IMPORTED_MODULES` when importing static modules into an executable. +shared or static modules or into the executable. -Example code for importing the two static modules `MyStaticModule1` and `MyStaticModule2` into an executable: +Example code for importing the static module `MyStaticModule1` into another library +or executable: \snippet uServices-staticmodules/main.cpp ImportStaticModuleIntoMain -Importing the static module `MyStaticModule` into a shared or static module looks like this: - -\snippet uServices-staticmodules/main.cpp ImportStaticModuleIntoLib - -Having a shared CppMicroServices library, the executable also needs some initialization code: - -\snippet uServices-staticmodules/main.cpp InitializeExecutable - -Note that shared (but not static) modules also need the `#US_INITIALIZE_MODULE` call when importing static modules. - ## Using a Static CppMicroServices Library -The CppMicroServices library can be build as a static library. In that case, creating shared modules is not supported. -If you create shared modules which link a static version of the CppMicroServices library, the runtime behavior is -undefined. - -In this usage scenario, every module will be statically build and linked to an executable. The executable needs to -import all the static modules, just like above: - -\snippet uServices-staticmodules/main.cpp ImportStaticModuleIntoMain +The CppMicroServices library can be build as a static library. In that case, creating shared +modules is not supported. If you create shared modules which link a static version of the +CppMicroServices library, the runtime behavior is undefined. -However, it can omit the `#US_INITIALIZE_MODULE` macro call (the module context from the CppMicroServices library -will be shared across all modules and the executable). +In this usage scenario, every module will be statically build and linked to an executable: -# A Note About The Module Context {#MicroServices_StaticModules_Context} +\snippet uServices-staticmodules/main.cpp ImportStaticModuleIntoMain2 -Modules using the CppMicroServices API frequently need a `ModuleContext` object to query, retrieve, and register services. -Static modules will never get their own module context but will share the context with their importing module or -executable. Therefore, the importing module or executable needs to ensure the availability of such a context (by using -the `#US_INITIALIZE_MODULE` or `#US_INITIALIZE_EXECUTABLE` macro). +Note that the first `#US_IMPORT_MODULE` call imports the static CppMicroServices library. +Then the `MyStaticModule2` module is imported and finally, the +executable itself is initialized (this is necessary if the executable itself is +a C++ Micro Services module). -\note The CppMicroServices library will *always* provide a module context, independent of its library build mode. +# A Note About Import Ordering {#MicroServices_StaticModules_Order} -So in a completely statically build application, the CppMicroServices library provides a global module context for all -imported modules and the executable. +Although static linking reduces the number of shared libraries, the statically +linked modules are still represented internally by distinct Module and ModuleContext +objects. In a shared module scenario, the linker dependencies define the load order +of modules and hence the order of their initialization and ModuleActivator::Load() +invocations. In the static case, the order of the `#US_IMPORT_MODULE` macro calls +defines the initialization and activation order. In case of a statically built +CppMicroServices library, it is therefore important to always import the static +CppMicroServices library into the executable first, and the executable itself +(if it needs to be initialized) last. diff --git a/Core/CppMicroServices/core/doc/doxygen/MicroServices_TheModuleContext.md b/Core/CppMicroServices/core/doc/doxygen/MicroServices_TheModuleContext.md index f35054b556..d3dd740cc0 100644 --- a/Core/CppMicroServices/core/doc/doxygen/MicroServices_TheModuleContext.md +++ b/Core/CppMicroServices/core/doc/doxygen/MicroServices_TheModuleContext.md @@ -1,37 +1,38 @@ The Module Context {#MicroServices_TheModuleContext} =================== In the context of the C++ Micro Services library, we will call all supported "shared library" types (DLL, DSO, DyLib, etc.) uniformly a *module*. A module accesses the C++ Micro Services API via a -ModuleContext object. While multiple modules could use the same ModuleContext, it is highly recommended -that each module gets its own (this will enable module specific service usage tracking and also allows -the C++ Micro Services framework to properly cleanup resources after a module has been unloaded). +ModuleContext object, which is specific to each module. ### Creating a ModuleContext To create a ModuleContext object for a specific library, you have two options. If your project uses CMake as the build system, use the supplied `#usFunctionGenerateModuleInit` CMake function to automatically create a source file and add it to your module's sources: ~~~{.cpp} set(module_srcs ) -usFunctionGenerateModuleInit(module_srcs - NAME "My Module" - LIBRARY_NAME "mylibname" - ) +usFunctionGenerateModuleInit(module_srcs) add_library(mylib ${module_srcs}) +set_property(TARGET ${mylib} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=mylib) ~~~ +You also need to specify a unique module name by using the `US_MODULE_NAME` compile definition as +shown in the last line. The module name must be a valid C identifier and in the case of +executables is required to be defined to `main`. + If you do not use CMake, you have to add a call to the macro `#US_INITIALIZE_MODULE` in one of the source files of your module: \snippet uServices-modulecontext/main.cpp InitializeModule ### Getting a ModuleContext To retrieve the module specific ModuleContext object from anywhere in your module, use the `#GetModuleContext` function: \snippet uServices-modulecontext/main.cpp GetModuleContext -Please note that the call to `#GetModuleContext` will fail if you did not create a module specific context. +Please note that trying to use `#GetModuleContext` without proper initialization code +in the using shared library while either lead to compile or rumtime errors. diff --git a/Core/CppMicroServices/core/doc/doxygen/standalone/BuildInstructions.md b/Core/CppMicroServices/core/doc/doxygen/standalone/BuildInstructions.md index 0a5f6fb275..201410319c 100644 --- a/Core/CppMicroServices/core/doc/doxygen/standalone/BuildInstructions.md +++ b/Core/CppMicroServices/core/doc/doxygen/standalone/BuildInstructions.md @@ -1,58 +1,55 @@ Build Instructions {#BuildInstructions} ================== The C++ Micro Services library provides [CMake][cmake] build scripts which allow the generation of platform and IDE specific project files. The library should compile on many different platforms. Below is a list of tested compiler/OS combinations: - GCC 4.6 (Ubuntu 12.04) - GCC 4.8 (Ubuntu 13.10) - Clang 3.2 (Ubuntu 13.10) - Clang (MacOS X 10.8 and 10.9) - Visual Studio 2008 SP1, 2010, 2012, 2013 (Windows 7) Prerequisites ------------- - [CMake][cmake] 2.8 (Visual Studio 2010 and 2012 users should use the latest CMake version available) Configuring the Build --------------------- When building the C++ Micro Services library, you have a few configuration options at hand. ### General build options - **CMAKE_INSTALL_PREFIX** The installation path. - **US_BUILD_SHARED_LIBS** Specify if the library should be build shared or static. See \ref MicroServices_StaticModules for detailed information about static CppMicroServices modules. - **US_BUILD_TESTING** Build unit tests and code snippets. - **US_ENABLE_AUTOLOADING_SUPPORT** Enable auto-loading of modules located in special sub-directories. See \ref MicroServices_AutoLoading for detailed information about this feature. - **US_ENABLE_THREADING_SUPPORT** Enable the use of synchronization primitives (atomics and pthread mutexes or Windows primitives) to make the API thread-safe. If your application is not multi-threaded, turn this option OFF to get maximum performance. -- **US_ENABLE_RESOURCE_COMPRESSION (advanced)** - Enable compression of embedded resources. See \ref MicroServices_Resources for detailed information - about the resource system. ### Customizing naming conventions - **US_NAMESPACE** The default namespace is `us` but you may override this at will. - **US_HEADER_PREFIX** By default, all public headers have a "us" prefix. You may specify an arbitrary prefix to match your naming conventions. The above options are mainly useful when embedding the C++ Micro Services source code in your own library and you want to make it look like native source code. [cmake]: http://www.cmake.org diff --git a/Core/CppMicroServices/core/doc/doxygen/standalone/MicroServices_GettingStarted.md b/Core/CppMicroServices/core/doc/doxygen/standalone/MicroServices_GettingStarted.md index fec5a1105c..7530637dd4 100644 --- a/Core/CppMicroServices/core/doc/doxygen/standalone/MicroServices_GettingStarted.md +++ b/Core/CppMicroServices/core/doc/doxygen/standalone/MicroServices_GettingStarted.md @@ -1,49 +1,52 @@ Getting Started {#MicroServices_GettingStarted} =============== Projects which want to make use of the capabilities provided by the C++ Micro Services library need to set-up the correct include paths and link dependencies. Further, each executable or shared library which needs a ModuleContext instance must contain specific -initialization code. +initialization code and must be compiled with a unique `US_MODULE_NAME` pre-processor +definition. In case of executables, the value is required to be `main`, e.g. compile +the executable with `-DUS_MODULE_NAME=main`. The C++ Micro Services library provides \ref MicroServicesCMake "CMake utility functions" for CMake based projects but there are no restrictions on the type of build system used for a project. CMake based projects -------------------- To easily set-up include paths and linker dependencies, use the common `find_package` mechanism provided by CMake: \dontinclude examples/CMakeLists.txt \skip project \until include_directories The CMake code above sets up a basic project (called CppMicroServicesExamples) and tries to find the CppMicroServices package and subsequently to set the necessary include directories. Building a shared library might then look like this: \dontinclude examples/dictionaryservice/CMakeLists.txt \until target_link The call to `#usFunctionGenerateModuleInit` generates the proper module initialization -code and provides access to the module specific ModuleContext instance. +code and provides access to the module specific ModuleContext instance. Further, the +`set_property`command sets the `US_MODULE_NAME` definition. Makefile based projects ----------------------- The following Makefile is located at examples/makefile/Makefile and demonstrates a minimal build script: \include makefile/Makefile The variable `CppMicroServices_ROOT` is an environment variable and must be set to the CppMicroServices installation directory prior to invoking `make`. The module initialization code for the `libmodule.so` shared library is generated by using the `#US_INITIALIZE_MODULE` pre-processor macro at the end of the `module.cpp` source file (any source file compiled into the module would do): \dontinclude makefile/module.cpp \skip usModuleInitialization \until US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-activator/main.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-activator/main.cpp index c4ba6c25e7..8b9feebd56 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-activator/main.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-activator/main.cpp @@ -1,27 +1,27 @@ #include US_USE_NAMESPACE //! [0] class MyActivator : public ModuleActivator { public: void Load(ModuleContext* /*context*/) { /* register stuff */ } void Unload(ModuleContext* /*context*/) { /* cleanup */ } }; -US_EXPORT_MODULE_ACTIVATOR(mylibname, MyActivator) +US_EXPORT_MODULE_ACTIVATOR(MyActivator) //![0] int main(int /*argc*/, char* /*argv*/[]) { MyActivator ma; return 0; } diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-modulecontext/main.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-modulecontext/main.cpp index a793f3b343..a7b8bfd7d7 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-modulecontext/main.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-modulecontext/main.cpp @@ -1,28 +1,28 @@ #include //! [GetModuleContext] #include #include #include US_USE_NAMESPACE void RetrieveModuleContext() { ModuleContext* context = GetModuleContext(); Module* module = context->GetModule(); std::cout << "Module name: " << module->GetName() << " [id: " << module->GetModuleId() << "]\n"; } //! [GetModuleContext] //! [InitializeModule] #include -US_INITIALIZE_MODULE("My Module", "mylibname") +US_INITIALIZE_MODULE //! [InitializeModule] int main(int /*argc*/, char* /*argv*/[]) { RetrieveModuleContext(); return 0; } diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-registration/main.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-registration/main.cpp index be9666e635..511791e1ac 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-registration/main.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-registration/main.cpp @@ -1,119 +1,115 @@ #include #include #include #include US_USE_NAMESPACE struct InterfaceA { virtual ~InterfaceA() {} }; struct InterfaceB { virtual ~InterfaceB() {} }; struct InterfaceC { virtual ~InterfaceC() {} }; -US_DECLARE_SERVICE_INTERFACE(InterfaceA, "org.cppmicroservices.snippet.InterfaceA") -US_DECLARE_SERVICE_INTERFACE(InterfaceB, "org.cppmicroservices.snippet.InterfaceB") -US_DECLARE_SERVICE_INTERFACE(InterfaceC, "org.cppmicroservices.snippet.InterfaceC") - //! [1-1] class MyService : public InterfaceA {}; //! [1-1] //! [2-1] class MyService2 : public InterfaceA, public InterfaceB {}; //! [2-1] class MyActivator : public ModuleActivator { public: void Load(ModuleContext* context) { Register1(context); Register2(context); RegisterFactory1(context); RegisterFactory2(context); } void Register1(ModuleContext* context) { //! [1-2] MyService* myService = new MyService; context->RegisterService(myService); //! [1-2] } void Register2(ModuleContext* context) { //! [2-2] MyService2* myService = new MyService2; context->RegisterService(myService); //! [2-2] } void RegisterFactory1(ModuleContext* context) { //! [f1] class MyServiceFactory : public ServiceFactory { virtual InterfaceMap GetService(Module* /*module*/, const ServiceRegistrationBase& /*registration*/) { MyService* myService = new MyService; return MakeInterfaceMap(myService); } virtual void UngetService(Module* /*module*/, const ServiceRegistrationBase& /*registration*/, const InterfaceMap& service) { delete ExtractInterface(service); } }; MyServiceFactory* myServiceFactory = new MyServiceFactory; context->RegisterService(myServiceFactory); //! [f1] } void RegisterFactory2(ModuleContext* context) { //! [f2] class MyServiceFactory : public ServiceFactory { virtual InterfaceMap GetService(Module* /*module*/, const ServiceRegistrationBase& /*registration*/) { MyService2* myService = new MyService2; return MakeInterfaceMap(myService); } virtual void UngetService(Module* /*module*/, const ServiceRegistrationBase& /*registration*/, const InterfaceMap& service) { delete ExtractInterface(service); } }; MyServiceFactory* myServiceFactory = new MyServiceFactory; context->RegisterService(static_cast(myServiceFactory)); //! [f2] // In the RegisterService call above, we could remove the static_cast because local types // are not considered in template argument type deduction and hence the compiler choose // the correct RegisterService(ServiceFactory*) overload. However, local types are // usually the exception and using a non-local type for the service factory would make the // compiler choose RegisterService(Impl*) instead, unless we use the static_cast. } void Unload(ModuleContext* /*context*/) { /* cleanup */ } }; -US_EXPORT_MODULE_ACTIVATOR(mylibname, MyActivator) +US_EXPORT_MODULE_ACTIVATOR(MyActivator) int main(int /*argc*/, char* /*argv*/[]) { MyActivator ma; return 0; } diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-resources/main.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-resources/main.cpp index 45eaa61658..577ac85242 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-resources/main.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-resources/main.cpp @@ -1,85 +1,85 @@ #include #include #include #include #include #include US_USE_NAMESPACE void resourceExample() { //! [1] // Get this module's Module object Module* module = GetModuleContext()->GetModule(); ModuleResource resource = module->GetResource("config.properties"); if (resource.IsValid()) { // Create a ModuleResourceStream object ModuleResourceStream resourceStream(resource); // Read the contents line by line std::string line; while (std::getline(resourceStream, line)) { // Process the content std::cout << line << std::endl; } } else { // Error handling } //! [1] } void parseComponentDefinition(std::istream&) { } void extenderPattern() { //! [2] // Get all loaded modules std::vector modules = ModuleRegistry::GetLoadedModules(); // Check if a module defines a "service-component" property // and use its value to retrieve an embedded resource containing // a component description. for(std::size_t i = 0; i < modules.size(); ++i) { Module* const module = modules[i]; std::string componentPath = module->GetProperty("service-component").ToString(); if (!componentPath.empty()) { ModuleResource componentResource = module->GetResource(componentPath); if (!componentResource.IsValid() || componentResource.IsDir()) continue; // Create a std::istream compatible object and parse the // component description. ModuleResourceStream resStream(componentResource); parseComponentDefinition(resStream); } } //! [2] } int main(int /*argc*/, char* /*argv*/[]) { //! [0] ModuleContext* moduleContext = GetModuleContext(); Module* module = moduleContext->GetModule(); // List all XML files in the config directory std::vector xmlFiles = module->FindResources("config", "*.xml", false); // Find the resource named vertex_shader.txt starting at the root directory std::vector shaders = module->FindResources("", "vertex_shader.txt", true); //! [0] return 0; } #include -US_INITIALIZE_EXECUTABLE("uServices-snippet-resources") +US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-servicetracker/main.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-servicetracker/main.cpp index 5961f956da..4cbf68249b 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-servicetracker/main.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-servicetracker/main.cpp @@ -1,110 +1,113 @@ #include #include US_USE_NAMESPACE struct IFooService {}; -US_DECLARE_SERVICE_INTERFACE(IFooService, "org.cppmicroservices.snippets.IFooService") - ///! [tt] struct MyTrackedClass { /* ... */ }; //! [tt] //! [ttt] struct MyTrackedClassTraits : public TrackedTypeTraitsBase { static bool IsValid(const TrackedType&) { // Dummy implementation return true; } static void Dispose(TrackedType&) {} static TrackedType DefaultValue() { return TrackedType(); } }; //! [ttt] //! [customizer] struct MyTrackingCustomizer : public ServiceTrackerCustomizer { virtual MyTrackedClass AddingService(const ServiceReferenceType&) { return MyTrackedClass(); } virtual void ModifiedService(const ServiceReferenceType&, MyTrackedClass) { } virtual void RemovedService(const ServiceReferenceType&, MyTrackedClass) { } }; //! [customizer] struct MyTrackingPointerCustomizer : public ServiceTrackerCustomizer { virtual MyTrackedClass* AddingService(const ServiceReferenceType&) { return new MyTrackedClass(); } virtual void ModifiedService(const ServiceReferenceType&, MyTrackedClass*) { } virtual void RemovedService(const ServiceReferenceType&, MyTrackedClass*) { } }; // For compilation test purposes only struct MyTrackingCustomizerVoid : public ServiceTrackerCustomizer { virtual MyTrackedClass AddingService(const ServiceReferenceType&) { return MyTrackedClass(); } virtual void ModifiedService(const ServiceReferenceType&, MyTrackedClass) { } virtual void RemovedService(const ServiceReferenceType&, MyTrackedClass) { } }; int main(int /*argc*/, char* /*argv*/[]) { { //! [tracker] MyTrackingCustomizer myCustomizer; ServiceTracker tracker(GetModuleContext(), &myCustomizer); //! [tracker] } { //! [tracker2] MyTrackingPointerCustomizer myCustomizer; ServiceTracker > tracker(GetModuleContext(), &myCustomizer); //! [tracker2] } // For compilation test purposes only MyTrackingCustomizerVoid myCustomizer2; - ServiceTracker tracker2(GetModuleContext(), &myCustomizer2); - ServiceTracker > tracker3(GetModuleContext()); + try + { + ServiceTracker tracker2(GetModuleContext(), &myCustomizer2); + ServiceTracker > tracker3(GetModuleContext()); + } + catch (const us::ServiceException&) + {} return 0; } #include -US_INITIALIZE_EXECUTABLE("uServices-modulecontext") +US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.h b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.h index 8f993b075c..bcd0726e97 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.h +++ b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.h @@ -1,63 +1,61 @@ #ifndef SINGLETONONE_H #define SINGLETONONE_H #include #include #include //![s1] class SingletonOne { public: static SingletonOne& GetInstance(); // Just some member int a; private: SingletonOne(); ~SingletonOne(); // Disable copy constructor and assignment operator. SingletonOne(const SingletonOne&); SingletonOne& operator=(const SingletonOne&); }; //![s1] class SingletonTwoService; //![ss1] class SingletonOneService { public: // This will return a SingletonOneService instance with the // lowest service id at the time this method was called the first // time and returned a non-null value (which is usually the instance // which was registered first). A null-pointer is returned if no // instance was registered yet. static SingletonOneService* GetInstance(); int a; private: // Only our module activator class should be able to instantiate // a SingletonOneService object. friend class MyActivator; SingletonOneService(); ~SingletonOneService(); // Disable copy constructor and assignment operator. SingletonOneService(const SingletonOneService&); SingletonOneService& operator=(const SingletonOneService&); }; - -US_DECLARE_SERVICE_INTERFACE(SingletonOneService, "org.cppmicroservices.snippet.SingletonOneService") //![ss1] #endif // SINGLETONONE_H diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.h b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.h index 8185e509e0..03429d51a9 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.h +++ b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.h @@ -1,48 +1,46 @@ #ifndef SINGLETONTWO_H #define SINGLETONTWO_H #include #include class SingletonTwo { public: static SingletonTwo& GetInstance(); int b; private: SingletonTwo(); ~SingletonTwo(); // Disable copy constructor and assignment operator. SingletonTwo(const SingletonTwo&); SingletonTwo& operator=(const SingletonTwo&); }; class SingletonTwoService { public: static SingletonTwoService* GetInstance(); int b; private: friend class MyActivator; SingletonTwoService(); ~SingletonTwoService(); // Disable copy constructor and assignment operator. SingletonTwoService(const SingletonTwoService&); SingletonTwoService& operator=(const SingletonTwoService&); }; -US_DECLARE_SERVICE_INTERFACE(SingletonTwoService, "org.cppmicroservices.snippet.SingletonTwoService") - #endif // SINGLETONTWO_H diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/files.cmake b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/files.cmake index 7de7f5c5ad..d203a5547f 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/files.cmake +++ b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/files.cmake @@ -1,10 +1,8 @@ set(snippet_src_files main.cpp SingletonOne.cpp SingletonTwo.cpp ) -usFunctionGenerateExecutableInit(snippet_src_files - IDENTIFIER "uServices_singleton" - ) +usFunctionGenerateModuleInit(snippet_src_files) diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/main.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/main.cpp index 76883c7e98..63ec571af4 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-singleton/main.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-singleton/main.cpp @@ -1,68 +1,73 @@ #include #include #include "SingletonOne.h" #include "SingletonTwo.h" US_USE_NAMESPACE class MyActivator : public ModuleActivator { public: + MyActivator() + : m_SingletonOne(NULL) + , m_SingletonTwo(NULL) + {} + //![0] void Load(ModuleContext* context) { // The Load() method of the module activator is called during static // initialization time of the shared library. // First create and register a SingletonTwoService instance. m_SingletonTwo = new SingletonTwoService; m_SingletonTwoReg = context->RegisterService(m_SingletonTwo); // Now the SingletonOneService constructor will get a valid // SingletonTwoService instance. m_SingletonOne = new SingletonOneService; m_SingletonOneReg = context->RegisterService(m_SingletonOne); } //![0] //![1] void Unload(ModuleContext* /*context*/) { // Services are automatically unregistered during unloading of // the shared library after the call to Unload(ModuleContext*) // has returned. // Since SingletonOneService needs a non-null SingletonTwoService // instance in its destructor, we explicitly unregister and delete the // SingletonOneService instance here. This way, the SingletonOneService // destructor will still get a valid SingletonTwoService instance. m_SingletonOneReg.Unregister(); delete m_SingletonOne; // For singletonTwoService, we could rely on the automatic unregistering // by the service registry and on automatic deletion if you used // smart pointer reference counting. You must not delete service instances // in this method without unregistering them first. m_SingletonTwoReg.Unregister(); delete m_SingletonTwo; } //![1] private: SingletonOneService* m_SingletonOne; SingletonTwoService* m_SingletonTwo; ServiceRegistration m_SingletonOneReg; ServiceRegistration m_SingletonTwoReg; }; -US_EXPORT_MODULE_ACTIVATOR(uServices_singleton, MyActivator) +US_EXPORT_MODULE_ACTIVATOR(MyActivator) int main() { } diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/MyStaticModule.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/MyStaticModule.cpp index 2984c93702..f61e009f6e 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/MyStaticModule.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/MyStaticModule.cpp @@ -1,39 +1,39 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include US_USE_NAMESPACE struct MyStaticModuleActivator : public ModuleActivator { void Load(ModuleContext* /*context*/) { std::cout << "Hello from a static module." << std::endl; } void Unload(ModuleContext* /*context*/) {} }; -US_EXPORT_MODULE_ACTIVATOR(MyStaticModule, MyStaticModuleActivator) +US_EXPORT_MODULE_ACTIVATOR(MyStaticModuleActivator) -US_INITIALIZE_MODULE("My Static Module", "MyStaticModule") +US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/files.cmake b/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/files.cmake index 2bf4c05a4b..989242adaa 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/files.cmake +++ b/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/files.cmake @@ -1,11 +1,12 @@ set(snippet_src_files main.cpp ) set(base_dir uServices-staticmodules) add_library(MyStaticModule STATIC ${base_dir}/MyStaticModule.cpp) -set_property(TARGET MyStaticModule APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE) +set_property(TARGET MyStaticModule APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE US_MODULE_NAME=MyStaticModule) +set_property(TARGET MyStaticModule PROPERTY US_MODULE_NAME MyStaticModule) set(snippet_link_libraries MyStaticModule ) diff --git a/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp b/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp index 404775b061..4a2a0c141f 100644 --- a/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp +++ b/Core/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp @@ -1,34 +1,39 @@ #include -US_USE_NAMESPACE - -//! [ImportStaticModuleIntoLib] -#include +US_BEGIN_NAMESPACE +struct ModuleActivator; +US_END_NAMESPACE -US_IMPORT_MODULE(MyStaticModule) -US_LOAD_IMPORTED_MODULES(HostingModule, MyStaticModule) -//! [ImportStaticModuleIntoLib] +US_USE_NAMESPACE // This is just for illustration purposes in code snippets -extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule1() { return NULL; } -extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule2() { return NULL; } -extern "C" ModuleActivator* _us_init_resources_MyStaticModule2() { return NULL; } +extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule1() { return 0; } +extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule2() { return 0; } +extern "C" void _us_import_module_initializer_MyStaticModule1() {} +extern "C" void _us_import_module_initializer_MyStaticModule2() {} //! [ImportStaticModuleIntoMain] #include US_IMPORT_MODULE(MyStaticModule1) -US_IMPORT_MODULE(MyStaticModule2) -US_IMPORT_MODULE_RESOURCES(MyStaticModule2) -US_LOAD_IMPORTED_MODULES_INTO_MAIN(MyStaticModule1 MyStaticModule2) //! [ImportStaticModuleIntoMain] +//! [ImportStaticModuleIntoMain2] +#include + +#ifndef US_BUILD_SHARED_LIBS +US_IMPORT_MODULE(CppMicroServices) +US_IMPORT_MODULE(MyStaticModule2) +US_INITIALIZE_STATIC_MODULE(main) +#endif +//! [ImportStaticModuleIntoMain2] + int main(int /*argc*/, char* /*argv*/[]) { return 0; } //! [InitializeExecutable] #include -US_INITIALIZE_EXECUTABLE("MyExecutable") +US_INITIALIZE_MODULE //! [InitializeExecutable] diff --git a/Core/CppMicroServices/core/examples/CMakeLists.txt b/Core/CppMicroServices/core/examples/CMakeLists.txt index cdeac59465..1144b939be 100644 --- a/Core/CppMicroServices/core/examples/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/CMakeLists.txt @@ -1,169 +1,176 @@ project(CoreExamples) cmake_minimum_required(VERSION 2.8) find_package(CppMicroServices NO_MODULE REQUIRED) include_directories(${CppMicroServices_INCLUDE_DIRS}) #----------------------------------------------------------------------------- # Set C/CXX flags #----------------------------------------------------------------------------- #if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CppMicroServices_CXX_FLAGS}") # set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CppMicroServices_CXX_FLAGS_RELEASE}") # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${CppMicroServices_CXX_FLAGS_DEBUG}") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CppMicroServices_C_FLAGS}") # set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${CppMicroServices_C_FLAGS_RELEASE}") # set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${CppMicroServices_C_FLAGS_DEBUG}") #endif() #----------------------------------------------------------------------------- # Init output directories #----------------------------------------------------------------------------- set(CoreExamples_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") set(CoreExamples_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") set(CoreExamples_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") foreach(_type ARCHIVE LIBRARY RUNTIME) if(NOT CMAKE_${_type}_OUTPUT_DIRECTORY) set(CMAKE_${_type}_OUTPUT_DIRECTORY ${CoreExamples_${_type}_OUTPUT_DIRECTORY}) endif() endforeach() function(CreateExample _name) - add_library(Example-${_name} SHARED ${ARGN}) + add_library(Example-${_name} ${ARGN}) + set_property(TARGET Example-${_name} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${_name}) + if(NOT US_BUILD_SHARED_LIBS) + set_property(TARGET Example-${_name} APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE) + endif() + if(${_name}_DEPENDS) foreach(_dep ${${_name}_DEPENDS}) include_directories(${PROJECT_SOURCE_DIR}/${_dep}) target_link_libraries(Example-${_name} Example-${_dep}) endforeach() endif() target_link_libraries(Example-${_name} ${CppMicroServices_LIBRARIES}) set_target_properties(Example-${_name} PROPERTIES LABELS Examples OUTPUT_NAME ${_name} ) endfunction() add_subdirectory(eventlistener) add_subdirectory(dictionaryservice) add_subdirectory(frenchdictionary) add_subdirectory(dictionaryclient) add_subdirectory(dictionaryclient2) add_subdirectory(dictionaryclient3) add_subdirectory(spellcheckservice) add_subdirectory(spellcheckclient) add_subdirectory(driver) #----------------------------------------------------------------------------- # Test if examples compile against an install tree and if the # Makefile example compiles #----------------------------------------------------------------------------- if(US_BUILD_TESTING) enable_testing() set(_example_tests ) if(WIN32) string(REGEX REPLACE "^.:" "" _install_prefix ${CMAKE_INSTALL_PREFIX}) else() set(_install_prefix ${CMAKE_INSTALL_PREFIX}) endif() set(_install_dir "${CppMicroServices_BINARY_DIR}/install_test/${_install_prefix}") add_test(NAME usInstallCleanTest COMMAND ${CMAKE_COMMAND} -E remove_directory "${_install_dir}") add_test(NAME usInstallTest WORKING_DIRECTORY ${CppMicroServices_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} --build ${CppMicroServices_BINARY_DIR} --target install) + COMMAND ${CMAKE_COMMAND} --build ${CppMicroServices_BINARY_DIR} --config $ --target install) set_tests_properties(usInstallTest PROPERTIES ENVIRONMENT "DESTDIR=${CppMicroServices_BINARY_DIR}/install_test" DEPENDS usInstallCleanTest) set(_examples_binary_dir "${CppMicroServices_BINARY_DIR}/examples_build") add_test(NAME usExamplesCleanTest COMMAND ${CMAKE_COMMAND} -E remove_directory "${_examples_binary_dir}") add_test(NAME usExamplesCreateDirTest COMMAND ${CMAKE_COMMAND} -E make_directory "${_examples_binary_dir}") set_tests_properties(usExamplesCreateDirTest PROPERTIES DEPENDS usExamplesCleanTest) list(APPEND _example_tests usInstallCleanTest usInstallTest usExamplesCleanTest usExamplesCreateDirTest) if(CMAKE_CONFIGURATION_TYPES) foreach(config ${CMAKE_CONFIGURATION_TYPES}) add_test(NAME usExamplesConfigureTest-${config} CONFIGURATIONS ${config} WORKING_DIRECTORY ${_examples_binary_dir} COMMAND ${CMAKE_COMMAND} -D CMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} -D CMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} + -D BUILD_SHARED_LIBS:BOOL=${US_BUILD_SHARED_LIBS} -G ${CMAKE_GENERATOR} "-DCppMicroServices_DIR:PATH=${_install_dir}/${AUXILIARY_CMAKE_INSTALL_DIR}" "${CMAKE_CURRENT_LIST_DIR}") set_tests_properties(usExamplesConfigureTest-${config} PROPERTIES DEPENDS "usInstallTest;usExamplesCreateDirTest") add_test(NAME usExamplesBuildTest-${config} CONFIGURATIONS ${config} WORKING_DIRECTORY ${_examples_binary_dir} COMMAND ${CMAKE_COMMAND} --build . --config ${config}) set_tests_properties(usExamplesBuildTest-${config} PROPERTIES DEPENDS usExamplesConfigureTest-${config}) list(APPEND _example_tests usExamplesConfigureTest-${config} usExamplesBuildTest-${config}) endforeach() else() add_test(NAME usExamplesConfigureTest-${CMAKE_BUILD_TYPE} WORKING_DIRECTORY ${_examples_binary_dir} COMMAND ${CMAKE_COMMAND} -D CMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -D CMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} -D CMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} + -D BUILD_SHARED_LIBS:BOOL=${US_BUILD_SHARED_LIBS} -G ${CMAKE_GENERATOR} "-DCppMicroServices_DIR:PATH=${_install_dir}/${AUXILIARY_CMAKE_INSTALL_DIR}" "${CMAKE_CURRENT_LIST_DIR}") set_tests_properties(usExamplesConfigureTest-${CMAKE_BUILD_TYPE} PROPERTIES DEPENDS "usInstallTest;usExamplesCreateDirTest") add_test(NAME usExamplesBuildTest-${CMAKE_BUILD_TYPE} WORKING_DIRECTORY ${_examples_binary_dir} COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}) set_tests_properties(usExamplesBuildTest-${CMAKE_BUILD_TYPE} PROPERTIES DEPENDS usExamplesConfigureTest-${CMAKE_BUILD_TYPE}) list(APPEND _example_tests usExamplesConfigureTest-${CMAKE_BUILD_TYPE} usExamplesBuildTest-${CMAKE_BUILD_TYPE}) endif() # The makefile is Linux specific, so only try to build the Makefile example # if we are on a proper system - if(UNIX AND NOT APPLE) + if(UNIX AND NOT APPLE AND US_BUILD_SHARED_LIBS) find_program(MAKE_COMMAND NAMES make gmake) find_program(CXX_COMMAND NAMES g++) mark_as_advanced(MAKE_COMMAND CXX_COMMAND) if(MAKE_COMMAND AND CXX_COMMAND) add_test(NAME usMakefileExampleCleanTest WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/makefile COMMAND ${MAKE_COMMAND} clean) add_test(NAME usMakefileExampleTest WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/makefile COMMAND ${MAKE_COMMAND}) set_tests_properties(usMakefileExampleTest PROPERTIES DEPENDS "usMakefileExampleCleanTest;usInstallTest" - ENVIRONMENT "CppMicroServices_ROOT=${CppMicroServices_BINARY_DIR}/install_test${CMAKE_INSTALL_PREFIX};CppMicroServices_CXX_FLAGS=${US_CXX_FLAGS}") + ENVIRONMENT "CppMicroServices_ROOT=${CppMicroServices_BINARY_DIR}/install_test${CMAKE_INSTALL_PREFIX};US_CXX_FLAGS=${US_CXX_FLAGS}") list(APPEND _example_tests usMakefileExampleCleanTest usMakefileExampleTest) endif() endif() if(US_TEST_LABELS) set_tests_properties(${_example_tests} PROPERTIES LABELS "${US_TEST_LABELS}") endif() endif() diff --git a/Core/CppMicroServices/core/examples/dictionaryclient/Activator.cpp b/Core/CppMicroServices/core/examples/dictionaryclient/Activator.cpp index fc3d47f37d..0da637fc4b 100644 --- a/Core/CppMicroServices/core/examples/dictionaryclient/Activator.cpp +++ b/Core/CppMicroServices/core/examples/dictionaryclient/Activator.cpp @@ -1,117 +1,121 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include "IDictionaryService.h" #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a module activator that uses a dictionary service to check for * the proper spelling of a word by check for its existence in the dictionary. * This modules uses the first service that it finds and does not monitor the * dynamic availability of the service (i.e., it does not listen for the arrival * or departure of dictionary services). When loading this module, the thread * calling the Load() method is used to read words from standard input. You can * stop checking words by entering an empty line, but to start checking words * again you must unload and then load the module again. */ class US_ABI_LOCAL Activator : public ModuleActivator { public: /** * Implements ModuleActivator::Load(). Queries for all available dictionary * services. If none are found it simply prints a message and returns, * otherwise it reads words from standard input and checks for their * existence from the first dictionary that it finds. * * \note It is very bad practice to use the calling thread to perform a lengthy * process like this; this is only done for the purpose of the tutorial. * * @param context the module context for this module. */ void Load(ModuleContext *context) { // Query for all service references matching any language. std::vector > refs = context->GetServiceReferences("(Language=*)"); if (!refs.empty()) { std::cout << "Enter a blank line to exit." << std::endl; // Loop endlessly until the user enters a blank line while (std::cin) { // Ask the user to enter a word. std::cout << "Enter word: "; std::string word; std::getline(std::cin, word); // If the user entered a blank line, then // exit the loop. if (word.empty()) { break; } // First, get a dictionary service and then check // if the word is correct. IDictionaryService* dictionary = context->GetService(refs.front()); if ( dictionary->CheckWord( word ) ) { std::cout << "Correct." << std::endl; } else { std::cout << "Incorrect." << std::endl; } // Unget the dictionary service. context->UngetService(refs.front()); } } else { std::cout << "Couldn't find any dictionary service..." << std::endl; } } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unget any used services. * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { // NOTE: The service is automatically released. } }; -US_EXPORT_MODULE_ACTIVATOR(dictionaryclient, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //![Activator] diff --git a/Core/CppMicroServices/core/examples/dictionaryclient/CMakeLists.txt b/Core/CppMicroServices/core/examples/dictionaryclient/CMakeLists.txt index bd610a3686..ed36e4caee 100644 --- a/Core/CppMicroServices/core/examples/dictionaryclient/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/dictionaryclient/CMakeLists.txt @@ -1,8 +1,6 @@ set(_srcs Activator.cpp) -usFunctionGenerateModuleInit(_srcs - NAME "Dictionary Client" - LIBRARY_NAME "dictionaryclient") +usFunctionGenerateModuleInit(_srcs) set(dictionaryclient_DEPENDS dictionaryservice) CreateExample(dictionaryclient ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/dictionaryclient2/Activator.cpp b/Core/CppMicroServices/core/examples/dictionaryclient2/Activator.cpp index 28d2e14276..5c221098c7 100644 --- a/Core/CppMicroServices/core/examples/dictionaryclient2/Activator.cpp +++ b/Core/CppMicroServices/core/examples/dictionaryclient2/Activator.cpp @@ -1,214 +1,218 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include "IDictionaryService.h" #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a module activator that uses a dictionary service to check for * the proper spelling of a word by checking for its existence in the * dictionary. This module is more complex than the module in Example 3 because * it monitors the dynamic availability of the dictionary services. In other * words, if the service it is using departs, then it stops using it gracefully, * or if it needs a service and one arrives, then it starts using it * automatically. As before, the module uses the first service that it finds and * uses the calling thread of the Load() method to read words from standard * input. You can stop checking words by entering an empty line, but to start * checking words again you must unload and then load the module again. */ class US_ABI_LOCAL Activator : public ModuleActivator { public: Activator() : m_context(NULL) , m_dictionary(NULL) {} /** * Implements ModuleActivator::Load(). Adds itself as a listener for service * events, then queries for available dictionary services. If any * dictionaries are found it gets a reference to the first one available and * then starts its "word checking loop". If no dictionaries are found, then * it just goes directly into its "word checking loop", but it will not be * able to check any words until a dictionary service arrives; any arriving * dictionary service will be automatically used by the client if a * dictionary is not already in use. Once it has dictionary, it reads words * from standard input and checks for their existence in the dictionary that * it is using. * * \note It is very bad practice to use the calling thread to perform a * lengthy process like this; this is only done for the purpose of * the tutorial. * * @param context the module context for this module. */ void Load(ModuleContext *context) { m_context = context; { // Use your favorite thread library to synchronize member // variable access within this scope while registering // the service listener and performing our initial // dictionary service lookup since we // don't want to receive service events when looking up the // dictionary service, if one exists. // MutexLocker lock(&m_mutex); // Listen for events pertaining to dictionary services. m_context->AddServiceListener(this, &Activator::ServiceChanged, std::string("(&(") + ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")" + "(Language=*))"); // Query for any service references matching any language. std::vector > refs = context->GetServiceReferences("(Language=*)"); // If we found any dictionary services, then just get // a reference to the first one so we can use it. if (!refs.empty()) { m_ref = refs.front(); m_dictionary = m_context->GetService(m_ref); } } std::cout << "Enter a blank line to exit." << std::endl; // Loop endlessly until the user enters a blank line while (std::cin) { // Ask the user to enter a word. std::cout << "Enter word: "; std::string word; std::getline(std::cin, word); // If the user entered a blank line, then // exit the loop. if (word.empty()) { break; } // If there is no dictionary, then say so. else if (m_dictionary == NULL) { std::cout << "No dictionary available." << std::endl; } // Otherwise print whether the word is correct or not. else if (m_dictionary->CheckWord( word )) { std::cout << "Correct." << std::endl; } else { std::cout << "Incorrect." << std::endl; } } } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unget any used services. * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { // NOTE: The service is automatically released. } /** * Implements ServiceListener.serviceChanged(). Checks to see if the service * we are using is leaving or tries to get a service if we need one. * * @param event the fired service event. */ void ServiceChanged(const ServiceEvent event) { // Use your favorite thread library to synchronize this // method with the Load() method. // MutexLocker lock(&m_mutex); // If a dictionary service was registered, see if we // need one. If so, get a reference to it. if (event.GetType() == ServiceEvent::REGISTERED) { if (!m_ref) { // Get a reference to the service object. m_ref = event.GetServiceReference(); m_dictionary = m_context->GetService(m_ref); } } // If a dictionary service was unregistered, see if it // was the one we were using. If so, unget the service // and try to query to get another one. else if (event.GetType() == ServiceEvent::UNREGISTERING) { if (event.GetServiceReference() == m_ref) { // Unget service object and null references. m_context->UngetService(m_ref); m_ref = 0; m_dictionary = NULL; // Query to see if we can get another service. std::vector > refs; try { refs = m_context->GetServiceReferences("(Language=*)"); } catch (const std::invalid_argument& e) { std::cout << e.what() << std::endl; } if (!refs.empty()) { // Get a reference to the first service object. m_ref = refs.front(); m_dictionary = m_context->GetService(m_ref); } } } } private: // Module context ModuleContext* m_context; // The service reference being used ServiceReference m_ref; // The service object being used IDictionaryService* m_dictionary; }; -US_EXPORT_MODULE_ACTIVATOR(dictionaryclient2, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //![Activator] diff --git a/Core/CppMicroServices/core/examples/dictionaryclient2/CMakeLists.txt b/Core/CppMicroServices/core/examples/dictionaryclient2/CMakeLists.txt index 7e7524a088..acd00f2a3c 100644 --- a/Core/CppMicroServices/core/examples/dictionaryclient2/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/dictionaryclient2/CMakeLists.txt @@ -1,8 +1,6 @@ set(_srcs Activator.cpp) -usFunctionGenerateModuleInit(_srcs - NAME "Dictionary Client 2" - LIBRARY_NAME "dictionaryclient2") +usFunctionGenerateModuleInit(_srcs) set(dictionaryclient2_DEPENDS dictionaryservice) CreateExample(dictionaryclient2 ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/dictionaryclient3/Activator.cpp b/Core/CppMicroServices/core/examples/dictionaryclient3/Activator.cpp index a97524435b..0c709e38fc 100644 --- a/Core/CppMicroServices/core/examples/dictionaryclient3/Activator.cpp +++ b/Core/CppMicroServices/core/examples/dictionaryclient3/Activator.cpp @@ -1,142 +1,146 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include "IDictionaryService.h" #include #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a module activator that uses a dictionary * service to check for the proper spelling of a word by * checking for its existence in the dictionary. This module * uses a service tracker to dynamically monitor the availability * of a dictionary service, instead of providing a custom service * listener as in Example 4. The module uses the service returned * by the service tracker, which is selected based on a ranking * algorithm defined by the C++ Micro Services library. * Again, the calling thread of the Load() method is used to read * words from standard input, checking its existence in the dictionary. * You can stop checking words by entering an empty line, but * to start checking words again you must unload and then load * the module again. */ class US_ABI_LOCAL Activator : public ModuleActivator { public: Activator() : m_context(NULL) , m_tracker(NULL) {} /** * Implements ModuleActivator::Load(). Creates a service * tracker to monitor dictionary services and starts its "word * checking loop". It will not be able to check any words until * the service tracker finds a dictionary service; any discovered * dictionary service will be automatically used by the client. * It reads words from standard input and checks for their * existence in the discovered dictionary. * * \note It is very bad practice to use the calling thread to perform a * lengthy process like this; this is only done for the purpose of * the tutorial. * * @param context the module context for this module. */ void Load(ModuleContext *context) { m_context = context; // Create a service tracker to monitor dictionary services. m_tracker = new ServiceTracker( m_context, LDAPFilter(std::string("(&(") + ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")" + "(Language=*))") ); m_tracker->Open(); std::cout << "Enter a blank line to exit." << std::endl; // Loop endlessly until the user enters a blank line while (std::cin) { // Ask the user to enter a word. std::cout << "Enter word: "; std::string word; std::getline(std::cin, word); // Get the selected dictionary, if available. IDictionaryService* dictionary = m_tracker->GetService(); // If the user entered a blank line, then // exit the loop. if (word.empty()) { break; } // If there is no dictionary, then say so. else if (dictionary == NULL) { std::cout << "No dictionary available." << std::endl; } // Otherwise print whether the word is correct or not. else if (dictionary->CheckWord(word)) { std::cout << "Correct." << std::endl; } else { std::cout << "Incorrect." << std::endl; } } // This automatically closes the tracker delete m_tracker; } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unget any used services. * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { } private: // Module context ModuleContext* m_context; // The service tracker ServiceTracker* m_tracker; }; -US_EXPORT_MODULE_ACTIVATOR(dictionaryclient3, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //![Activator] diff --git a/Core/CppMicroServices/core/examples/dictionaryclient3/CMakeLists.txt b/Core/CppMicroServices/core/examples/dictionaryclient3/CMakeLists.txt index 44984fbd6e..bdf08eaa5e 100644 --- a/Core/CppMicroServices/core/examples/dictionaryclient3/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/dictionaryclient3/CMakeLists.txt @@ -1,8 +1,6 @@ set(_srcs Activator.cpp) -usFunctionGenerateModuleInit(_srcs - NAME "Dictionary Client 3" - LIBRARY_NAME "dictionaryclient3") +usFunctionGenerateModuleInit(_srcs) set(dictionaryclient3_DEPENDS dictionaryservice) CreateExample(dictionaryclient3 ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/dictionaryservice/Activator.cpp b/Core/CppMicroServices/core/examples/dictionaryservice/Activator.cpp index a7289f7a42..fb3ec2da27 100644 --- a/Core/CppMicroServices/core/examples/dictionaryservice/Activator.cpp +++ b/Core/CppMicroServices/core/examples/dictionaryservice/Activator.cpp @@ -1,116 +1,120 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include "IDictionaryService.h" #include #include #include #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a module activator that uses the module * context to register an English language dictionary service * with the C++ Micro Services registry during static initialization * of the module. The dictionary service interface is * defined in a separate file and is implemented by a nested class. */ class US_ABI_LOCAL Activator : public ModuleActivator { private: /** * A private inner class that implements a dictionary service; * see IDictionaryService for details of the service. */ class DictionaryImpl : public IDictionaryService { // The set of words contained in the dictionary. std::set m_dictionary; public: DictionaryImpl() { m_dictionary.insert("welcome"); m_dictionary.insert("to"); m_dictionary.insert("the"); m_dictionary.insert("micro"); m_dictionary.insert("services"); m_dictionary.insert("tutorial"); } /** * Implements IDictionaryService::CheckWord(). Determines * if the passed in word is contained in the dictionary. * @param word the word to be checked. * @return true if the word is in the dictionary, * false otherwise. **/ bool CheckWord(const std::string& word) { std::string lword(word); std::transform(lword.begin(), lword.end(), lword.begin(), ::tolower); return m_dictionary.find(lword) != m_dictionary.end(); } }; std::auto_ptr m_dictionaryService; public: /** * Implements ModuleActivator::Load(). Registers an * instance of a dictionary service using the module context; * attaches properties to the service that can be queried * when performing a service look-up. * @param context the context for the module. */ void Load(ModuleContext* context) { m_dictionaryService.reset(new DictionaryImpl); ServiceProperties props; props["Language"] = std::string("English"); context->RegisterService(m_dictionaryService.get(), props); } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unregister any registered services. * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { // NOTE: The service is automatically unregistered } }; -US_EXPORT_MODULE_ACTIVATOR(dictionaryservice, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //![Activator] diff --git a/Core/CppMicroServices/core/examples/dictionaryservice/CMakeLists.txt b/Core/CppMicroServices/core/examples/dictionaryservice/CMakeLists.txt index ae21e3b14c..c904b4e9ac 100644 --- a/Core/CppMicroServices/core/examples/dictionaryservice/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/dictionaryservice/CMakeLists.txt @@ -1,26 +1,31 @@ # The library name for the module set(_lib_name dictionaryservice) # A list of source code files set(_srcs Activator.cpp IDictionaryService.cpp ) # Generate module initialization code -usFunctionGenerateModuleInit(_srcs - NAME "Dictionary Service" - LIBRARY_NAME ${_lib_name}) +usFunctionGenerateModuleInit(_srcs) # Create the library -add_library(Example-${_lib_name} SHARED ${_srcs}) +add_library(Example-${_lib_name} ${_srcs}) + +# Add the required compile definitions +set_property(TARGET Example-${_lib_name} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=${_lib_name}) +if(NOT US_BUILD_SHARED_LIBS) + set_property(TARGET Example-${_lib_name} APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE) +endif() + # Link the CppMicroServices library target_link_libraries(Example-${_lib_name} ${CppMicroServices_LIBRARIES}) set_target_properties(Example-${_lib_name} PROPERTIES LABELS Examples OUTPUT_NAME ${_lib_name} ) #CreateExample(dictionaryservice ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h b/Core/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h index df806d843f..df085b5d71 100644 --- a/Core/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h +++ b/Core/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h @@ -1,58 +1,60 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef IDICTIONARYSERVICE_H #define IDICTIONARYSERVICE_H //! [service] #include #include +#ifdef US_BUILD_SHARED_LIBS #ifdef Example_dictionaryservice_EXPORTS - #define DICTIONAYSERVICE_EXPORT US_ABI_EXPORT + #define DICTIONARYSERVICE_EXPORT US_ABI_EXPORT #else - #define DICTIONAYSERVICE_EXPORT US_ABI_IMPORT + #define DICTIONARYSERVICE_EXPORT US_ABI_IMPORT +#endif +#else +#define DICTIONARYSERVICE_EXPORT US_ABI_EXPORT #endif /** * A simple service interface that defines a dictionary service. * A dictionary service simply verifies the existence of a word. **/ -struct DICTIONAYSERVICE_EXPORT IDictionaryService +struct DICTIONARYSERVICE_EXPORT IDictionaryService { // Out-of-line virtual desctructor for proper dynamic cast // support with older versions of gcc. virtual ~IDictionaryService(); /** * Check for the existence of a word. * @param word the word to be checked. * @return true if the word is in the dictionary, * false otherwise. **/ virtual bool CheckWord(const std::string& word) = 0; }; - -US_DECLARE_SERVICE_INTERFACE(IDictionaryService, "IDictionaryService/1.0") //! [service] #endif // DICTIONARYSERVICE_H diff --git a/Core/CppMicroServices/core/examples/driver/CMakeLists.txt b/Core/CppMicroServices/core/examples/driver/CMakeLists.txt index fe0c928f9a..e88c04d5df 100644 --- a/Core/CppMicroServices/core/examples/driver/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/driver/CMakeLists.txt @@ -1,16 +1,19 @@ if(WIN32) string(REPLACE "/" "\\\\" CMAKE_LIBRARY_OUTPUT_DIRECTORY_NATIVE ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) string(REPLACE "/" "\\\\" CMAKE_RUNTIME_OUTPUT_DIRECTORY_NATIVE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) else() set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_NATIVE ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_NATIVE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/us${PROJECT_NAME}DriverConfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/us${PROJECT_NAME}DriverConfig.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_executable(us${PROJECT_NAME}Driver main.cpp) +if(NOT US_BUILD_SHARED_LIBS) + target_link_libraries(us${PROJECT_NAME}Driver Example-eventlistener Example-dictionaryservice Example-dictionaryclient) +endif() target_link_libraries(us${PROJECT_NAME}Driver ${CppMicroServices_LIBRARIES}) diff --git a/Core/CppMicroServices/core/examples/driver/main.cpp b/Core/CppMicroServices/core/examples/driver/main.cpp index 89ee1c7ce9..3c87a5768a 100644 --- a/Core/CppMicroServices/core/examples/driver/main.cpp +++ b/Core/CppMicroServices/core/examples/driver/main.cpp @@ -1,298 +1,309 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include +#include #include "usCoreExamplesDriverConfig.h" #if defined(US_PLATFORM_POSIX) #include #elif defined(US_PLATFORM_WINDOWS) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #else #error Unsupported platform #endif #include #include #include #include #include #include #include US_USE_NAMESPACE #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif -std::map GetExampleModules() +std::vector GetExampleModules() { - std::map names; - names.insert(std::make_pair("Event Listener", "eventlistener")); - names.insert(std::make_pair("Dictionary Service", "dictionaryservice")); - names.insert(std::make_pair("French Dictionary", "frenchdictionary")); - names.insert(std::make_pair("Dictionary Client", "dictionaryclient")); - names.insert(std::make_pair("Dictionary Client 2", "dictionaryclient2")); - names.insert(std::make_pair("Dictionary Client 3", "dictionaryclient3")); - names.insert(std::make_pair("Spell Check Service", "spellcheckservice")); - names.insert(std::make_pair("Spell Check Client", "spellcheckclient")); + std::vector names; +#ifdef US_BUILD_SHARED_LIBS + names.push_back("eventlistener"); + names.push_back("dictionaryservice"); + names.push_back("frenchdictionary"); + names.push_back("dictionaryclient"); + names.push_back("dictionaryclient2"); + names.push_back("dictionaryclient3"); + names.push_back("spellcheckservice"); + names.push_back("spellcheckclient"); +#endif return names; } int main(int /*argc*/, char** /*argv*/) { char cmd[256]; - std::map availableModules = GetExampleModules(); + std::vector availableModules = GetExampleModules(); /* module path -> lib handle */ std::map libraryHandles; SharedLibrary sharedLib(LIB_PATH, ""); std::cout << "> "; while(std::cin.getline(cmd, sizeof(cmd))) { std::string strCmd(cmd); if (strCmd == "q") { break; } else if (strCmd == "h") { std::cout << std::left << std::setw(15) << "h" << " This help text\n" << std::setw(15) << "l " << " Load the module with id or name \n" << std::setw(15) << "u " << " Unload the module with id \n" << std::setw(15) << "s" << " Print status information\n" << std::setw(15) << "q" << " Quit\n" << std::flush; } else if (strCmd.find("l ") != std::string::npos) { std::string idOrName; idOrName.assign(strCmd.begin()+2, strCmd.end()); std::stringstream ss(idOrName); long int id = -1; ss >> id; if (id > 0) { Module* module = ModuleRegistry::GetModule(id); if (!module) { std::cout << "Error: unknown id" << std::endl; } else if (module->IsLoaded()) { std::cout << "Info: module already loaded" << std::endl; } else { try { std::map::iterator libIter = libraryHandles.find(module->GetLocation()); if (libIter != libraryHandles.end()) { libIter->second.Load(); } else { // The module has been loaded previously due to a // linker dependency SharedLibrary libHandle(module->GetLocation()); libHandle.Load(); libraryHandles.insert(std::make_pair(libHandle.GetFilePath(), libHandle)); } } catch (const std::exception& e) { std::cout << e.what() << std::endl; } } } else { Module* module = ModuleRegistry::GetModule(idOrName); if (!module) { try { std::map::iterator libIter = libraryHandles.find(sharedLib.GetFilePath(idOrName)); if (libIter != libraryHandles.end()) { libIter->second.Load(); } else { bool libFound = false; - for (std::map::const_iterator availableModuleIter = availableModules.begin(); + for (std::vector::const_iterator availableModuleIter = availableModules.begin(); availableModuleIter != availableModules.end(); ++availableModuleIter) { - if (availableModuleIter->second == idOrName) + if (*availableModuleIter == idOrName) { libFound = true; } } if (!libFound) { std::cout << "Error: unknown example module" << std::endl; } else { SharedLibrary libHandle(LIB_PATH, idOrName); libHandle.Load(); libraryHandles.insert(std::make_pair(libHandle.GetFilePath(), libHandle)); } } std::vector modules = ModuleRegistry::GetModules(); for (std::vector::const_iterator moduleIter = modules.begin(); moduleIter != modules.end(); ++moduleIter) { - availableModules.erase((*moduleIter)->GetName()); + availableModules.erase(std::remove(availableModules.begin(), availableModules.end(), (*moduleIter)->GetName()), + availableModules.end()); } } catch (const std::exception& e) { std::cout << e.what() << std::endl; } } else if (!module->IsLoaded()) { try { const std::string modulePath = module->GetLocation(); std::map::iterator libIter = libraryHandles.find(modulePath); if (libIter != libraryHandles.end()) { libIter->second.Load(); } else { SharedLibrary libHandle(LIB_PATH, idOrName); libHandle.Load(); libraryHandles.insert(std::make_pair(libHandle.GetFilePath(), libHandle)); } } catch (const std::exception& e) { std::cout << e.what() << std::endl; } } else if (module) { std::cout << "Info: module already loaded" << std::endl; } } } else if (strCmd.find("u ") != std::string::npos) { std::stringstream ss(strCmd); ss.ignore(2); long int id = -1; ss >> id; if (id == 1) { std::cout << "Info: Unloading not possible" << std::endl; } else { Module* const module = ModuleRegistry::GetModule(id); if (module) { std::map::iterator libIter = libraryHandles.find(module->GetLocation()); if (libIter == libraryHandles.end()) { std::cout << "Info: Unloading not possible. The module was loaded by a dependent module." << std::endl; } else { try { libIter->second.Unload(); // Check if it has really been unloaded if (module->IsLoaded()) { std::cout << "Info: The module is still referenced by another loaded module. It will be unloaded when all dependent modules are unloaded." << std::endl; } } catch (const std::exception& e) { std::cout << e.what() << std::endl; } } } else { std::cout << "Error: unknown id" << std::endl; } } } else if (strCmd == "s") { std::vector modules = ModuleRegistry::GetModules(); std::cout << std::left; std::cout << "Id | " << std::setw(20) << "Name" << " | " << std::setw(9) << "Status" << std::endl; std::cout << "-----------------------------------\n"; - for (std::map::const_iterator nameIter = availableModules.begin(); + for (std::vector::const_iterator nameIter = availableModules.begin(); nameIter != availableModules.end(); ++nameIter) { - std::cout << " - | " << std::setw(20) << nameIter->second << " | " << std::setw(9) << "-" << std::endl; + std::cout << " - | " << std::setw(20) << *nameIter << " | " << std::setw(9) << "-" << std::endl; } for (std::vector::const_iterator moduleIter = modules.begin(); moduleIter != modules.end(); ++moduleIter) { std::cout << std::right << std::setw(2) << (*moduleIter)->GetModuleId() << std::left << " | "; std::cout << std::setw(20) << (*moduleIter)->GetName() << " | "; std::cout << std::setw(9) << ((*moduleIter)->IsLoaded() ? "LOADED" : "UNLOADED"); std::cout << std::endl; } } else { std::cout << "Unknown command: " << strCmd << " (type 'h' for help)" << std::endl; } std::cout << "> "; } return 0; } + +#ifndef US_BUILD_SHARED_LIBS +US_IMPORT_MODULE(CppMicroServices) +US_IMPORT_MODULE(eventlistener) +US_IMPORT_MODULE(dictionaryservice) +US_IMPORT_MODULE(dictionaryclient) +#endif diff --git a/Core/CppMicroServices/core/examples/eventlistener/Activator.cpp b/Core/CppMicroServices/core/examples/eventlistener/Activator.cpp index d7b9ae245f..af97ea85de 100644 --- a/Core/CppMicroServices/core/examples/eventlistener/Activator.cpp +++ b/Core/CppMicroServices/core/examples/eventlistener/Activator.cpp @@ -1,90 +1,94 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a simple module that utilizes the CppMicroServices's * event mechanism to listen for service events. Upon receiving a service event, * it prints out the event's details. */ class Activator : public ModuleActivator { private: /** * Implements ModuleActivator::Load(). Prints a message and adds a member * function to the module context as a service listener. * * @param context the framework context for the module. */ void Load(ModuleContext* context) { std::cout << "Starting to listen for service events." << std::endl; context->AddServiceListener(this, &Activator::ServiceChanged); } /** * Implements ModuleActivator::Unload(). Prints a message and removes the * member function from the module context as a service listener. * * @param context the framework context for the module. */ void Unload(ModuleContext* context) { context->RemoveServiceListener(this, &Activator::ServiceChanged); std::cout << "Stopped listening for service events." << std::endl; // Note: It is not required that we remove the listener here, // since the framework will do it automatically anyway. } /** * Prints the details of any service event from the framework. * * @param event the fired service event. */ void ServiceChanged(const ServiceEvent event) { - std::string objectClass = ref_any_cast >(event.GetServiceReference().GetProperty(ServiceConstants::OBJECTCLASS())).front(); + std::string objectClass = ref_any_cast >(event.GetServiceReference().GetProperty(ServiceConstants::OBJECTCLASS())).front(); if (event.GetType() == ServiceEvent::REGISTERED) { std::cout << "Ex1: Service of type " << objectClass << " registered." << std::endl; } else if (event.GetType() == ServiceEvent::UNREGISTERING) { std::cout << "Ex1: Service of type " << objectClass << " unregistered." << std::endl; } else if (event.GetType() == ServiceEvent::MODIFIED) { std::cout << "Ex1: Service of type " << objectClass << " modified." << std::endl; } } }; -US_EXPORT_MODULE_ACTIVATOR(eventlistener, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //! [Activator] diff --git a/Core/CppMicroServices/core/examples/eventlistener/CMakeLists.txt b/Core/CppMicroServices/core/examples/eventlistener/CMakeLists.txt index 2c6dce49be..b5103996bf 100644 --- a/Core/CppMicroServices/core/examples/eventlistener/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/eventlistener/CMakeLists.txt @@ -1,7 +1,5 @@ set(_srcs Activator.cpp) -usFunctionGenerateModuleInit(_srcs - NAME "Event Listener" - LIBRARY_NAME "eventlistener") +usFunctionGenerateModuleInit(_srcs) CreateExample(eventlistener ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/frenchdictionary/Activator.cpp b/Core/CppMicroServices/core/examples/frenchdictionary/Activator.cpp index cb70512826..5943c14187 100644 --- a/Core/CppMicroServices/core/examples/frenchdictionary/Activator.cpp +++ b/Core/CppMicroServices/core/examples/frenchdictionary/Activator.cpp @@ -1,119 +1,122 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include "IDictionaryService.h" #include #include -#include #include #include #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a module activator that uses the module * context to register a French language dictionary service * with the C++ Micro Services registry during static initialization * of the module. The dictionary service interface is * defined in Example 2 (dictionaryservice) and is implemented by a * nested class. This class is identical to the class in Example 2, * except that the dictionary contains French words. */ class US_ABI_LOCAL Activator : public ModuleActivator { private: /** * A private inner class that implements a dictionary service; * see DictionaryService for details of the service. */ class DictionaryImpl : public IDictionaryService { // The set of words contained in the dictionary. std::set m_dictionary; public: DictionaryImpl() { m_dictionary.insert("bienvenue"); m_dictionary.insert("au"); m_dictionary.insert("tutoriel"); m_dictionary.insert("micro"); m_dictionary.insert("services"); } /** * Implements DictionaryService.checkWord(). Determines * if the passed in word is contained in the dictionary. * @param word the word to be checked. * @return true if the word is in the dictionary, * false otherwise. **/ bool CheckWord(const std::string& word) { std::string lword(word); std::transform(lword.begin(), lword.end(), lword.begin(), ::tolower); return m_dictionary.find(lword) != m_dictionary.end(); } }; std::auto_ptr m_dictionaryService; public: /** * Implements ModuleActivator::Load(). Registers an * instance of a dictionary service using the module context; * attaches properties to the service that can be queried * when performing a service look-up. * @param context the context for the module. */ void Load(ModuleContext* context) { m_dictionaryService.reset(new DictionaryImpl); ServiceProperties props; props["Language"] = std::string("French"); context->RegisterService(m_dictionaryService.get(), props); } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unregister any registered services. * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { // NOTE: The service is automatically unregistered } }; -US_EXPORT_MODULE_ACTIVATOR(frenchdictionary, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //![Activator] diff --git a/Core/CppMicroServices/core/examples/frenchdictionary/CMakeLists.txt b/Core/CppMicroServices/core/examples/frenchdictionary/CMakeLists.txt index d5c0a649dd..79c3031d56 100644 --- a/Core/CppMicroServices/core/examples/frenchdictionary/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/frenchdictionary/CMakeLists.txt @@ -1,8 +1,6 @@ set(_srcs Activator.cpp) -usFunctionGenerateModuleInit(_srcs - NAME "French Dictionary" - LIBRARY_NAME "frenchdictionary") +usFunctionGenerateModuleInit(_srcs) set(frenchdictionary_DEPENDS dictionaryservice) CreateExample(frenchdictionary ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/makefile/IDictionaryService.h b/Core/CppMicroServices/core/examples/makefile/IDictionaryService.h index 1f003f955d..9ffce159fc 100644 --- a/Core/CppMicroServices/core/examples/makefile/IDictionaryService.h +++ b/Core/CppMicroServices/core/examples/makefile/IDictionaryService.h @@ -1,33 +1,30 @@ #ifndef IDICTIONARYSERVICE_H #define IDICTIONARYSERVICE_H #include #include #ifdef MODULE_EXPORTS #define MODULE_EXPORT US_ABI_EXPORT #else #define MODULE_EXPORT US_ABI_IMPORT #endif /** * A simple service interface that defines a dictionary service. * A dictionary service simply verifies the existence of a word. **/ struct MODULE_EXPORT IDictionaryService { virtual ~IDictionaryService(); /** * Check for the existence of a word. * @param word the word to be checked. * @return true if the word is in the dictionary, * false otherwise. **/ virtual bool CheckWord(const std::string& word) = 0; }; - -US_DECLARE_SERVICE_INTERFACE(IDictionaryService, "IDictionaryService/1.0") - #endif // DICTIONARYSERVICE_H diff --git a/Core/CppMicroServices/core/examples/makefile/Makefile b/Core/CppMicroServices/core/examples/makefile/Makefile index 860712a72a..c88e6e62ce 100644 --- a/Core/CppMicroServices/core/examples/makefile/Makefile +++ b/Core/CppMicroServices/core/examples/makefile/Makefile @@ -1,26 +1,26 @@ CXX = g++ CXXFLAGS = -g -Wall -Wno-unused -pedantic -fPIC $(US_CXX_FLAGS) LDFLAGS = -Wl,-rpath="$(CppMicroServices_ROOT)/lib" -Wl,-rpath=. LDLIBS = -lCppMicroServices INCLUDEDIRS = -I"$(CppMicroServices_ROOT)/include/CppMicroServices" LIBDIRS = -L"$(CppMicroServices_ROOT)/lib/CppMicroServices" -L. all : main libmodule.so main: libmodule.so main.o $(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(INCLUDEDIRS) $(LIBDIRS) $(LDLIBS) -lmodule libmodule.so: module.o IDictionaryService.o $(CXX) -shared -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(INCLUDEDIRS) $(LIBDIRS) $(LDLIBS) main.o: main.cpp - $(CXX) $(CXXFLAGS) $(INCLUDEDIRS) -c $< -o $@ + $(CXX) $(CXXFLAGS) -DUS_MODULE_NAME=main $(INCLUDEDIRS) -c $< -o $@ %.o: %.cpp - $(CXX) $(CXXFLAGS) -DMODULE_EXPORTS $(INCLUDEDIRS) -c $< -o $@ + $(CXX) $(CXXFLAGS) -DMODULE_EXPORTS -DUS_MODULE_NAME=module $(INCLUDEDIRS) -c $< -o $@ .PHONY : clean clean: rm -f *.o diff --git a/Core/CppMicroServices/core/examples/makefile/main.cpp b/Core/CppMicroServices/core/examples/makefile/main.cpp index fe66aa18b3..fb1193b1a2 100644 --- a/Core/CppMicroServices/core/examples/makefile/main.cpp +++ b/Core/CppMicroServices/core/examples/makefile/main.cpp @@ -1,25 +1,25 @@ #include #include #include #include "IDictionaryService.h" US_USE_NAMESPACE int main(int /*argc*/, char* /*argv*/[]) { ServiceReference dictionaryServiceRef = GetModuleContext()->GetServiceReference(); if (dictionaryServiceRef) { IDictionaryService* dictionaryService = GetModuleContext()->GetService(dictionaryServiceRef); if (dictionaryService) { std::cout << "Dictionary contains 'Tutorial': " << dictionaryService->CheckWord("Tutorial") << std::endl; } } } #include -US_INITIALIZE_EXECUTABLE("main") +US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/examples/makefile/module.cpp b/Core/CppMicroServices/core/examples/makefile/module.cpp index e5260d7720..81342b9a47 100644 --- a/Core/CppMicroServices/core/examples/makefile/module.cpp +++ b/Core/CppMicroServices/core/examples/makefile/module.cpp @@ -1,102 +1,102 @@ #include #include #include #include #include "IDictionaryService.h" #include #include #include US_USE_NAMESPACE /** * This class implements a module activator that uses the module * context to register an English language dictionary service * with the C++ Micro Services registry during static initialization * of the module. The dictionary service interface is * defined in a separate file and is implemented by a nested class. */ class US_ABI_LOCAL MyActivator : public ModuleActivator { private: /** * A private inner class that implements a dictionary service; * see DictionaryService for details of the service. */ class DictionaryImpl : public IDictionaryService { // The set of words contained in the dictionary. std::set m_Dictionary; public: DictionaryImpl() { m_Dictionary.insert("welcome"); m_Dictionary.insert("to"); m_Dictionary.insert("the"); m_Dictionary.insert("micro"); m_Dictionary.insert("services"); m_Dictionary.insert("tutorial"); } /** * Implements IDictionaryService::CheckWord(). Determines * if the passed in word is contained in the dictionary. * * @param word the word to be checked. * @return true if the word is in the dictionary, * false otherwise. **/ bool CheckWord(const std::string& word) { std::string lword(word); std::transform(lword.begin(), lword.end(), lword.begin(), ::tolower); return m_Dictionary.find(lword) != m_Dictionary.end(); } }; std::auto_ptr m_DictionaryService; public: /** * Implements ModuleActivator::Load(). Registers an * instance of a dictionary service using the module context; * attaches properties to the service that can be queried * when performing a service look-up. * * @param context the context for the module. */ void Load(ModuleContext* context) { m_DictionaryService.reset(new DictionaryImpl); ServiceProperties props; props["Language"] = std::string("English"); context->RegisterService(m_DictionaryService.get(), props); } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unregister any registered services. * * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { // NOTE: The service is automatically unregistered } }; -US_EXPORT_MODULE_ACTIVATOR(module, MyActivator) +US_EXPORT_MODULE_ACTIVATOR(MyActivator) #include -US_INITIALIZE_MODULE("My Module", "module") +US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/examples/spellcheckclient/Activator.cpp b/Core/CppMicroServices/core/examples/spellcheckclient/Activator.cpp index 7014fe46fa..ff4b09ec8b 100644 --- a/Core/CppMicroServices/core/examples/spellcheckclient/Activator.cpp +++ b/Core/CppMicroServices/core/examples/spellcheckclient/Activator.cpp @@ -1,143 +1,147 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include "ISpellCheckService.h" #include #include #include #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a module that uses a spell checker * service to check the spelling of a passage. This module * is essentially identical to Example 5, in that it uses the * Service Tracker to monitor the dynamic availability of the * spell checker service. When loading this module, the thread * calling the Load() method is used to read passages from * standard input. You can stop spell checking passages by * entering an empty line, but to start spell checking again * you must un-load and then load the module again. **/ class US_ABI_LOCAL Activator : public ModuleActivator { public: Activator() : m_context(NULL) , m_tracker(NULL) {} /** * Implements ModuleActivator::Load(). Creates a service * tracker object to monitor spell checker services. Enters * a spell check loop where it reads passages from standard * input and checks their spelling using the spell checker service. * * \note It is very bad practice to use the calling thread to perform a * lengthy process like this; this is only done for the purpose of * the tutorial. * * @param context the module context for this module. */ void Load(ModuleContext *context) { m_context = context; // Create a service tracker to monitor spell check services. m_tracker = new ServiceTracker(m_context); m_tracker->Open(); std::cout << "Enter a blank line to exit." << std::endl; // Loop endlessly until the user enters a blank line while (std::cin) { // Ask the user to enter a passage. std::cout << "Enter passage: "; std::string passage; std::getline(std::cin, passage); // Get the selected spell check service, if available. ISpellCheckService* checker = m_tracker->GetService(); // If the user entered a blank line, then // exit the loop. if (passage.empty()) { break; } // If there is no spell checker, then say so. else if (checker == NULL) { std::cout << "No spell checker available." << std::endl; } // Otherwise check passage and print misspelled words. else { std::vector errors = checker->Check(passage); if (errors.empty()) { std::cout << "Passage is correct." << std::endl; } else { std::cout << "Incorrect word(s):" << std::endl; for (std::size_t i = 0; i < errors.size(); ++i) { std::cout << " " << errors[i] << std::endl; } } } } // This automatically closes the tracker delete m_tracker; } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unget any used services. * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { } private: // Module context ModuleContext* m_context; // The service tracker ServiceTracker* m_tracker; }; -US_EXPORT_MODULE_ACTIVATOR(spellcheckclient, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //![Activator] diff --git a/Core/CppMicroServices/core/examples/spellcheckclient/CMakeLists.txt b/Core/CppMicroServices/core/examples/spellcheckclient/CMakeLists.txt index 7e28ef4b12..4600cc22ac 100644 --- a/Core/CppMicroServices/core/examples/spellcheckclient/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/spellcheckclient/CMakeLists.txt @@ -1,8 +1,6 @@ set(_srcs Activator.cpp) -usFunctionGenerateModuleInit(_srcs - NAME "Spell Check Client" - LIBRARY_NAME "spellcheckclient") +usFunctionGenerateModuleInit(_srcs) set(spellcheckclient_DEPENDS spellcheckservice) CreateExample(spellcheckclient ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/spellcheckservice/Activator.cpp b/Core/CppMicroServices/core/examples/spellcheckservice/Activator.cpp index 1470d00a4f..816c20cf9e 100644 --- a/Core/CppMicroServices/core/examples/spellcheckservice/Activator.cpp +++ b/Core/CppMicroServices/core/examples/spellcheckservice/Activator.cpp @@ -1,225 +1,229 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ //! [Activator] #include "IDictionaryService.h" #include "ISpellCheckService.h" #include #include #include #include #include #include #include #include US_USE_NAMESPACE +namespace { + /** * This class implements a module that implements a spell * checker service. The spell checker service uses all available * dictionary services to check for the existence of words in * a given sentence. This module uses a ServiceTracker to * monitors the dynamic availability of dictionary services, * and to aggregate all available dictionary services as they * arrive and depart. The spell checker service is only registered * if there are dictionary services available, thus the spell * checker service will appear and disappear as dictionary * services appear and disappear, respectively. **/ class US_ABI_LOCAL Activator : public ModuleActivator, public ServiceTrackerCustomizer { private: /** * A private inner class that implements a spell check service; see * ISpellCheckService for details of the service. */ class SpellCheckImpl : public ISpellCheckService { private: typedef std::map, IDictionaryService*> RefToServiceType; RefToServiceType m_refToSvcMap; public: /** * Implements ISpellCheckService::Check(). Checks the given passage for * misspelled words. * * @param passage the passage to spell check. * @return A list of misspelled words. */ std::vector Check(const std::string& passage) { std::vector errorList; // No misspelled words for an empty string. if (passage.empty()) { return errorList; } // Tokenize the passage using spaces and punctuation. const char* delimiters = " ,.!?;:"; char* passageCopy = new char[passage.size()+1]; std::memcpy(passageCopy, passage.c_str(), passage.size()+1); char* pch = std::strtok(passageCopy, delimiters); { // Lock the m_refToSvcMap member using your favorite thread library here... // MutexLocker lock(&m_refToSvcMapMutex) // Loop through each word in the passage. while (pch) { std::string word(pch); bool correct = false; // Check each available dictionary for the current word. for (RefToServiceType::const_iterator i = m_refToSvcMap.begin(); (!correct) && (i != m_refToSvcMap.end()); ++i) { IDictionaryService* dictionary = i->second; if (dictionary->CheckWord(word)) { correct = true; } } // If the word is not correct, then add it // to the incorrect word list. if (!correct) { errorList.push_back(word); } pch = std::strtok(NULL, delimiters); } } delete[] passageCopy; return errorList; } std::size_t AddDictionary(const ServiceReference& ref, IDictionaryService* dictionary) { // Lock the m_refToSvcMap member using your favorite thread library here... // MutexLocker lock(&m_refToSvcMapMutex) m_refToSvcMap.insert(std::make_pair(ref, dictionary)); return m_refToSvcMap.size(); } std::size_t RemoveDictionary(const ServiceReference& ref) { // Lock the m_refToSvcMap member using your favorite thread library here... // MutexLocker lock(&m_refToSvcMapMutex) m_refToSvcMap.erase(ref); return m_refToSvcMap.size(); } }; virtual IDictionaryService* AddingService(const ServiceReference& reference) { IDictionaryService* dictionary = m_context->GetService(reference); std::size_t count = m_spellCheckService->AddDictionary(reference, dictionary); if (!m_spellCheckReg && count > 1) { m_spellCheckReg = m_context->RegisterService(m_spellCheckService.get()); } return dictionary; } virtual void ModifiedService(const ServiceReference& /*reference*/, IDictionaryService* /*service*/) { // do nothing } virtual void RemovedService(const ServiceReference& reference, IDictionaryService* /*service*/) { if (m_spellCheckService->RemoveDictionary(reference) < 2 && m_spellCheckReg) { m_spellCheckReg.Unregister(); m_spellCheckReg = 0; } } std::auto_ptr m_spellCheckService; ServiceRegistration m_spellCheckReg; ModuleContext* m_context; std::auto_ptr > m_tracker; public: Activator() : m_context(NULL) {} /** * Implements ModuleActivator::Load(). Registers an * instance of a dictionary service using the module context; * attaches properties to the service that can be queried * when performing a service look-up. * * @param context the context for the module. */ void Load(ModuleContext* context) { m_context = context; m_spellCheckService.reset(new SpellCheckImpl); m_tracker.reset(new ServiceTracker(context, this)); m_tracker->Open(); } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unregister any registered services * and release any used services. * * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { // NOTE: The service is automatically unregistered m_tracker->Close(); } }; -US_EXPORT_MODULE_ACTIVATOR(spellcheckservice, Activator) +} + +US_EXPORT_MODULE_ACTIVATOR(Activator) //![Activator] diff --git a/Core/CppMicroServices/core/examples/spellcheckservice/CMakeLists.txt b/Core/CppMicroServices/core/examples/spellcheckservice/CMakeLists.txt index a86d8153a6..6c38a331a8 100644 --- a/Core/CppMicroServices/core/examples/spellcheckservice/CMakeLists.txt +++ b/Core/CppMicroServices/core/examples/spellcheckservice/CMakeLists.txt @@ -1,8 +1,6 @@ set(_srcs Activator.cpp ISpellCheckService.cpp) -usFunctionGenerateModuleInit(_srcs - NAME "Spell Check Service" - LIBRARY_NAME "spellcheckservice") +usFunctionGenerateModuleInit(_srcs) set(spellcheckservice_DEPENDS dictionaryservice) CreateExample(spellcheckservice ${_srcs}) diff --git a/Core/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h b/Core/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h index 679235801b..0f4875d124 100644 --- a/Core/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h +++ b/Core/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h @@ -1,64 +1,66 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef ISPELLCHECKSERVICE_H #define ISPELLCHECKSERVICE_H //! [service] #include #include #include +#ifdef US_BUILD_SHARED_LIBS #ifdef Example_spellcheckservice_EXPORTS #define SPELLCHECKSERVICE_EXPORT US_ABI_EXPORT #else #define SPELLCHECKSERVICE_EXPORT US_ABI_IMPORT #endif +#else + #define SPELLCHECKSERVICE_EXPORT US_ABI_EXPORT +#endif /** * A simple service interface that defines a spell check service. A spell check * service checks the spelling of all words in a given passage. A passage is any * number of words separated by a space character and the following punctuation * marks: comma, period, exclamation mark, question mark, semi-colon, and colon. */ struct SPELLCHECKSERVICE_EXPORT ISpellCheckService { // Out-of-line virtual desctructor for proper dynamic cast // support with older versions of gcc. virtual ~ISpellCheckService(); /** * Checks a given passage for spelling errors. A passage is any number of * words separated by a space and any of the following punctuation marks: * comma (,), period (.), exclamation mark (!), question mark (?), * semi-colon (;), and colon(:). * * @param passage the passage to spell check. * @return A list of misspelled words. */ virtual std::vector Check(const std::string& passage) = 0; }; - -US_DECLARE_SERVICE_INTERFACE(ISpellCheckService, "ISpellCheckService/1.0") //! [service] //! #endif // ISPELLCHECKSERVICE_H diff --git a/Core/CppMicroServices/core/include/CMakeLists.txt b/Core/CppMicroServices/core/include/CMakeLists.txt index b06be463cd..4e39d58c68 100644 --- a/Core/CppMicroServices/core/include/CMakeLists.txt +++ b/Core/CppMicroServices/core/include/CMakeLists.txt @@ -1,47 +1,46 @@ #----------------------------------------------------------------------------- # Public header files #----------------------------------------------------------------------------- set(_public_headers usAny.h usLDAPProp.h usSharedData.h usSharedLibrary.h usShrinkableMap.h usShrinkableVector.h - usUncompressResourceData.h usLDAPFilter.h usPrototypeServiceFactory.h usServiceEvent.h usServiceEventListenerHook.h usServiceException.h usServiceFactory.h usServiceFindHook.h usServiceInterface.h usServiceListenerHook.h usServiceObjects.h usServiceProperties.h usServiceReference.h usServiceReferenceBase.h usServiceRegistration.h usServiceRegistrationBase.h usServiceTracker.h usServiceTrackerCustomizer.h usGetModuleContext.h usModule.h usModuleActivator.h usModuleContext.h usModuleEvent.h usModuleEventHook.h usModuleFindHook.h usModuleImport.h usModuleInfo.h usModuleInitialization.h usModuleRegistry.h usModuleResource.h usModuleResourceStream.h usModuleSettings.h usModuleVersion.h ) diff --git a/Core/CppMicroServices/core/include/usAny.h b/Core/CppMicroServices/core/include/usAny.h index d59848c720..bfa2250d35 100644 --- a/Core/CppMicroServices/core/include/usAny.h +++ b/Core/CppMicroServices/core/include/usAny.h @@ -1,397 +1,577 @@ /*============================================================================= Library: CppMicroServices Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. Extracted from Boost 1.46.1 and adapted for CppMicroServices. Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =========================================================================*/ #ifndef US_ANY_H #define US_ANY_H #include #include #include #include #include +#include #include #include US_BEGIN_NAMESPACE +class Any; + +US_Core_EXPORT std::string any_value_to_string(const Any& any); + +US_Core_EXPORT std::string any_value_to_json(const Any& val); +US_Core_EXPORT std::string any_value_to_json(const std::string& val); +US_Core_EXPORT std::string any_value_to_json(bool val); + +template +std::string any_value_to_string(const T& val) +{ + std::stringstream ss; + ss << val; + return ss.str(); +} + template -std::string any_value_to_string(const T& val); +std::string any_value_to_json(const T& val) +{ + return any_value_to_string(val); +} + +/** + * \internal + */ +template +std::string container_to_string(Iterator i1, Iterator i2) +{ + std::stringstream ss; + ss << "["; + const Iterator begin = i1; + for ( ; i1 != i2; ++i1) + { + if (i1 == begin) ss << any_value_to_string(*i1); + else ss << "," << any_value_to_string(*i1); + } + ss << "]"; + return ss.str(); +} + +/** + * \internal + */ +template +std::string container_to_json(Iterator i1, Iterator i2) +{ + std::stringstream ss; + ss << "["; + const Iterator begin = i1; + for ( ; i1 != i2; ++i1) + { + if (i1 == begin) ss << any_value_to_json(*i1); + else ss << "," << any_value_to_json(*i1); + } + ss << "]"; + return ss.str(); +} + +template +std::string any_value_to_string(const std::vector& vec) +{ + return container_to_string(vec.begin(), vec.end()); +} + +template +std::string any_value_to_json(const std::vector& vec) +{ + return container_to_json(vec.begin(), vec.end()); +} + +template +std::string any_value_to_string(const std::list& l) +{ + return container_to_string(l.begin(), l.end()); +} + +template +std::string any_value_to_json(const std::list& l) +{ + return container_to_json(l.begin(), l.end()); +} + +template +std::string any_value_to_string(const std::set& s) +{ + return container_to_string(s.begin(), s.end()); +} + +template +std::string any_value_to_json(const std::set& s) +{ + return container_to_json(s.begin(), s.end()); +} + +template +std::string any_value_to_string(const std::map& m); + +template +std::string any_value_to_string(const std::map& m); + +template +std::string any_value_to_json(const std::map& m); + +template +std::string any_value_to_json(const std::map& m); -US_Core_EXPORT std::string any_value_to_string(const std::vector& val); -US_Core_EXPORT std::string any_value_to_string(const std::list& val); /** * \ingroup MicroServicesUtils * * An Any class represents a general type and is capable of storing any type, supporting type-safe extraction * of the internally stored data. * * Code taken from the Boost 1.46.1 library. Original copyright by Kevlin Henney. Modified for CppMicroServices. */ class Any { public: /** * Creates an empty any type. */ Any(): _content(0) { } /** * Creates an Any which stores the init parameter inside. * * \param value The content of the Any * * Example: * \code * Any a(13); * Any a(string("12345")); * \endcode */ template Any(const ValueType& value) : _content(new Holder(value)) { } /** * Copy constructor, works with empty Anys and initialized Any values. * * \param other The Any to copy */ Any(const Any& other) : _content(other._content ? other._content->Clone() : 0) { } ~Any() { delete _content; } /** * Swaps the content of the two Anys. * * \param rhs The Any to swap this Any with. */ Any& Swap(Any& rhs) { std::swap(_content, rhs._content); return *this; } /** * Assignment operator for all types != Any. * * \param rhs The value which should be assigned to this Any. * * Example: * \code * Any a = 13; * Any a = string("12345"); * \endcode */ template Any& operator = (const ValueType& rhs) { Any(rhs).Swap(*this); return *this; } /** * Assignment operator for Any. * * \param rhs The Any which should be assigned to this Any. */ Any& operator = (const Any& rhs) { Any(rhs).Swap(*this); return *this; } /** * returns true if the Any is empty */ bool Empty() const { return !_content; } /** * Returns a string representation for the content. * * Custom types should either provide a std::ostream& operator<<(std::ostream& os, const CustomType& ct) * function or specialize the any_value_to_string template function for meaningful output. */ std::string ToString() const { return _content->ToString(); } + /** + * Returns a JSON representation for the content. + * + * Custom types should specialize the any_value_to_json template function for meaningful output. + */ + std::string ToJSON() const + { + return Empty() ? "null" : _content->ToJSON(); + } + /** * Returns the type information of the stored content. * If the Any is empty typeid(void) is returned. * It is suggested to always query an Any for its type info before trying to extract * data via an any_cast/ref_any_cast. */ const std::type_info& Type() const { return _content ? _content->Type() : typeid(void); } private: class Placeholder { public: virtual ~Placeholder() { } virtual std::string ToString() const = 0; + virtual std::string ToJSON() const = 0; virtual const std::type_info& Type() const = 0; virtual Placeholder* Clone() const = 0; }; template class Holder: public Placeholder { public: Holder(const ValueType& value) : _held(value) { } virtual std::string ToString() const { return any_value_to_string(_held); } + virtual std::string ToJSON() const + { + return any_value_to_json(_held); + } + virtual const std::type_info& Type() const { return typeid(ValueType); } virtual Placeholder* Clone() const { return new Holder(_held); } ValueType _held; private: // intentionally left unimplemented Holder& operator=(const Holder &); }; private: template friend ValueType* any_cast(Any*); template friend ValueType* unsafe_any_cast(Any*); Placeholder* _content; }; class BadAnyCastException : public std::bad_cast { public: BadAnyCastException(const std::string& msg = "") : std::bad_cast(), _msg(msg) {} ~BadAnyCastException() throw() {} virtual const char * what() const throw() { if (_msg.empty()) return "US_PREPEND_NAMESPACE(BadAnyCastException): " "failed conversion using US_PREPEND_NAMESPACE(any_cast)"; else return _msg.c_str(); } private: std::string _msg; }; /** * any_cast operator used to extract the ValueType from an Any*. Will return a pointer * to the stored value. * * Example Usage: * \code * MyType* pTmp = any_cast(pAny) * \endcode * Will return NULL if the cast fails, i.e. types don't match. */ template ValueType* any_cast(Any* operand) { return operand && operand->Type() == typeid(ValueType) ? &static_cast*>(operand->_content)->_held : 0; } /** * any_cast operator used to extract a const ValueType pointer from an const Any*. Will return a const pointer * to the stored value. * * Example Usage: * \code * const MyType* pTmp = any_cast(pAny) * \endcode * Will return NULL if the cast fails, i.e. types don't match. */ template const ValueType* any_cast(const Any* operand) { return any_cast(const_cast(operand)); } /** * any_cast operator used to extract a copy of the ValueType from an const Any&. * * Example Usage: * \code * MyType tmp = any_cast(anAny) * \endcode * Will throw a BadCastException if the cast fails. * Dont use an any_cast in combination with references, i.e. MyType& tmp = ... or const MyType& = ... * Some compilers will accept this code although a copy is returned. Use the ref_any_cast in * these cases. */ template ValueType any_cast(const Any& operand) { ValueType* result = any_cast(const_cast(&operand)); if (!result) throw BadAnyCastException("Failed to convert between const Any types"); return *result; } /** * any_cast operator used to extract a copy of the ValueType from an Any&. * * Example Usage: * \code * MyType tmp = any_cast(anAny) * \endcode * Will throw a BadCastException if the cast fails. * Dont use an any_cast in combination with references, i.e. MyType& tmp = ... or const MyType& tmp = ... * Some compilers will accept this code although a copy is returned. Use the ref_any_cast in * these cases. */ template ValueType any_cast(Any& operand) { ValueType* result = any_cast(&operand); if (!result) throw BadAnyCastException("Failed to convert between Any types"); return *result; } /** * ref_any_cast operator used to return a const reference to the internal data. * * Example Usage: * \code * const MyType& tmp = ref_any_cast(anAny); * \endcode */ template const ValueType& ref_any_cast(const Any & operand) { ValueType* result = any_cast(const_cast(&operand)); if (!result) throw BadAnyCastException("RefAnyCast: Failed to convert between const Any types"); return *result; } /** * ref_any_cast operator used to return a reference to the internal data. * * Example Usage: * \code * MyType& tmp = ref_any_cast(anAny); * \endcode */ template ValueType& ref_any_cast(Any& operand) { ValueType* result = any_cast(&operand); if (!result) throw BadAnyCastException("RefAnyCast: Failed to convert between Any types"); return *result; } /** * \internal * * The "unsafe" versions of any_cast are not part of the * public interface and may be removed at any time. They are * required where we know what type is stored in the any and can't * use typeid() comparison, e.g., when our types may travel across * different shared libraries. */ template ValueType* unsafe_any_cast(Any* operand) { return &static_cast*>(operand->_content)->_held; } /** * \internal * * The "unsafe" versions of any_cast are not part of the * public interface and may be removed at any time. They are * required where we know what type is stored in the any and can't * use typeid() comparison, e.g., when our types may travel across * different shared libraries. */ template const ValueType* unsafe_any_cast(const Any* operand) { return any_cast(const_cast(operand)); } -US_Core_EXPORT std::string any_value_to_string(const std::vector& val); -US_Core_EXPORT std::string any_value_to_string(const std::map& val); -template -std::string any_value_to_string(const T& val) +template +std::string any_value_to_string(const std::map& m) { std::stringstream ss; - ss << val; + ss << "{"; + typedef typename std::map::const_iterator Iterator; + Iterator i1 = m.begin(); + const Iterator begin = i1; + const Iterator end = m.end(); + for ( ; i1 != end; ++i1) + { + if (i1 == begin) ss << i1->first << " : " << i1->second.ToString(); + else ss << ", " << i1->first << " : " << i1->second.ToString(); + } + ss << "}"; + return ss.str(); +} + +template +std::string any_value_to_string(const std::map& m) +{ + std::stringstream ss; + ss << "{"; + typedef typename std::map::const_iterator Iterator; + Iterator i1 = m.begin(); + const Iterator begin = i1; + const Iterator end = m.end(); + for ( ; i1 != end; ++i1) + { + if (i1 == begin) ss << i1->first << " : " << i1->second; + else ss << ", " << i1->first << " : " << i1->second; + } + ss << "}"; + return ss.str(); +} + +template +std::string any_value_to_json(const std::map& m) +{ + std::stringstream ss; + ss << "{"; + typedef typename std::map::const_iterator Iterator; + Iterator i1 = m.begin(); + const Iterator begin = i1; + const Iterator end = m.end(); + for ( ; i1 != end; ++i1) + { + if (i1 == begin) ss << "\"" << i1->first << "\" : " << i1->second.ToJSON(); + else ss << ", " << "\"" << i1->first << "\" : " << i1->second.ToJSON(); + } + ss << "}"; + return ss.str(); +} + +template +std::string any_value_to_json(const std::map& m) +{ + std::stringstream ss; + ss << "{"; + typedef typename std::map::const_iterator Iterator; + Iterator i1 = m.begin(); + const Iterator begin = i1; + const Iterator end = m.end(); + for ( ; i1 != end; ++i1) + { + if (i1 == begin) ss << "\"" << i1->first << "\" : " << i1->second; + else ss << ", " << "\"" << i1->first << "\" : " << i1->second; + } + ss << "}"; return ss.str(); } US_END_NAMESPACE #endif // US_ANY_H diff --git a/Core/CppMicroServices/core/include/usGetModuleContext.h b/Core/CppMicroServices/core/include/usGetModuleContext.h index 394bd8b609..83e7f72a09 100644 --- a/Core/CppMicroServices/core/include/usGetModuleContext.h +++ b/Core/CppMicroServices/core/include/usGetModuleContext.h @@ -1,54 +1,56 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USGETMODULECONTEXT_H #define USGETMODULECONTEXT_H +#ifndef US_MODULE_NAME +#error Missing preprocessor define US_MODULE_NAME +#endif + #include +#include +#include +#include US_BEGIN_NAMESPACE class ModuleContext; /** * \ingroup MicroServices * * \brief Returns the module context of the calling module. * * This function allows easy access to the ModuleContext instance from * inside a C++ Micro Services module. * * \return The ModuleContext of the calling module. - * - * \internal - * - * Note that the corresponding function definition is provided for each - * module by the usModuleInit.cpp file. This file is customized via CMake - * configure_file(...) and automatically compiled into each module. - * Consequently, the GetModuleContext function is not exported, since each - * module gets its "own version". */ -US_ABI_LOCAL ModuleContext* GetModuleContext(); +static inline ModuleContext* GetModuleContext() +{ + return ModuleRegistry::GetModule(US_STR(US_MODULE_NAME))->GetModuleContext(); +} US_END_NAMESPACE #endif // USGETMODULECONTEXT_H diff --git a/Core/CppMicroServices/core/include/usModule.h b/Core/CppMicroServices/core/include/usModule.h index 6a873ff649..397ab0f18f 100644 --- a/Core/CppMicroServices/core/include/usModule.h +++ b/Core/CppMicroServices/core/include/usModule.h @@ -1,369 +1,367 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULE_H #define USMODULE_H #include "usModuleVersion.h" #include US_BEGIN_NAMESPACE class Any; class CoreModuleContext; struct ModuleInfo; class ModuleContext; class ModuleResource; class ModulePrivate; template class ServiceReference; typedef ServiceReference ServiceReferenceU; /** * \ingroup MicroServices * * Represents a CppMicroServices module. * *

* A %Module object is the access point to a CppMicroServices module. * Each CppMicroServices module has an associated %Module object. * *

* A module has unique identity, a long, chosen by the * framework. This identity does not change during the lifecycle of a module. * *

* A module can be in one of two states: *

    *
  • LOADED *
  • UNLOADED *
*

* You can determine the current state by using IsLoaded(). * *

* A module can only execute code when its state is LOADED. * An UNLOADED module is a * zombie and can only be reached because it was loaded before. However, * unloaded modules can be loaded again. * *

* The framework is the only entity that is allowed to create * %Module objects. * * @remarks This class is thread safe. */ class US_Core_EXPORT Module { public: /** * Returns the property key for looking up this module's id. * The property value is of type \c long. * * @return The id property key. */ static const std::string& PROP_ID(); /** * Returns the property key for looking up this module's name. * The property value is of type \c std::string. * * @return The name property key. */ static const std::string& PROP_NAME(); /** * Returns the property key for looking up this module's - * location the file system. + * location in the file system. * The property value is of type \c std::string. * * @return The location property key. */ static const std::string& PROP_LOCATION(); /** * Returns the property key with a value of \c module.version for looking * up this module's version identifier. * The property value is of type \c std::string. * * @return The version property key. */ static const std::string& PROP_VERSION(); /** * Returns the property key with a value of \c module.vendor for looking * up this module's vendor information. * The property value is of type \c std::string. * - * @return The version property key. + * @return The vendor property key. */ static const std::string& PROP_VENDOR(); /** * Returns the property key with a value of \c module.description for looking * up this module's description. * The property value is of type \c std::string. * - * @return The version property key. + * @return The description property key. */ static const std::string& PROP_DESCRIPTION(); /** * Returns the property key with a value of \c module.autoload_dir for looking * up this module's auto-load directory. * The property value is of type \c std::string. * - * @return The version property key. + * @return The auto-load directory property key. */ static const std::string& PROP_AUTOLOAD_DIR(); + /** + * Returns the property key with a value of \c module.autoloaded_modules for + * looking up this module's auto-load modules. + * The property value is of type \c std::vector and contains + * the file system locations for the auto-loaded modules triggered by this + * module. + * + * @return The auto-loaded modules property key. + */ + static const std::string& PROP_AUTOLOADED_MODULES(); + ~Module(); /** * Returns this module's current state. * *

* A module can be in only one state at any time. * * @return true if the module is LOADED * false if it is UNLOADED */ bool IsLoaded() const; /** * Returns this module's {@link ModuleContext}. The returned * ModuleContext can be used by the caller to act on behalf * of this module. * *

* If this module is not in the LOADED state, then this * module has no valid ModuleContext. This method will * return 0 if this module has no valid * ModuleContext. * * @return A ModuleContext for this module or * 0 if this module has no valid * ModuleContext. */ ModuleContext* GetModuleContext() const; /** * Returns this module's unique identifier. This module is assigned a unique * identifier by the framework when it was loaded. * *

* A module's unique identifier has the following attributes: *

    *
  • Is unique. *
  • Is a long. *
  • Its value is not reused for another module, even after a module is * unloaded. *
  • Does not change while a module remains loaded. *
  • Does not change when a module is reloaded. *
* *

* This method continues to return this module's unique identifier while * this module is in the UNLOADED state. * * @return The unique identifier of this module. */ long GetModuleId() const; /** * Returns this module's location. * *

* The location is the full path to the module's shared library. * This method continues to return this module's location * while this module is in the UNLOADED state. * * @return The string representation of this module's location. */ std::string GetLocation() const; /** * Returns the name of this module as specified by the * US_CREATE_MODULE CMake macro. The module * name together with a version must identify a unique module. * *

* This method continues to return this module's name while * this module is in the UNLOADED state. * * @return The name of this module. */ std::string GetName() const; /** * Returns the version of this module as specified by the * US_INITIALIZE_MODULE CMake macro. If this module does not have a * specified version then {@link ModuleVersion::EmptyVersion} is returned. * *

* This method continues to return this module's version while * this module is in the UNLOADED state. * * @return The version of this module. */ ModuleVersion GetVersion() const; /** * Returns the value of the specified property for this module. The * method returns an empty Any if the property is not found. * * @param key The name of the requested property. * @return The value of the requested property, or an empty string * if the property is undefined. * * @sa GetPropertyKeys() * @sa \ref MicroServices_ModuleProperties */ Any GetProperty(const std::string& key) const; /** * Returns a list of top-level property keys for this module. * * @return A list of available property keys. * * @sa \ref MicroServices_ModuleProperties */ std::vector GetPropertyKeys() const; /** * Returns this module's ServiceReference list for all services it * has registered or an empty list if this module has no registered * services. * * The list is valid at the time of the call to this method, however, * as the framework is a very dynamic environment, services can be * modified or unregistered at anytime. * * @return A list of ServiceReference objects for services this * module has registered. */ std::vector GetRegisteredServices() const; /** * Returns this module's ServiceReference list for all services it is * using or returns an empty list if this module is not using any * services. A module is considered to be using a service if its use * count for that service is greater than zero. * * The list is valid at the time of the call to this method, however, * as the framework is a very dynamic environment, services can be * modified or unregistered at anytime. * * @return A list of ServiceReference objects for all services this * module is using. */ std::vector GetServicesInUse() const; /** * Returns the resource at the specified \c path in this module. * The specified \c path is always relative to the root of this module and may * begin with '/'. A path value of "/" indicates the root of this module. * - * \note In case of other modules being statically linked into this module, - * the \c path can be ambiguous and returns the first resource matching the - * provided \c path according to the order of the static module names in the - * #US_LOAD_IMPORTED_MODULES macro. - * * @param path The path name of the resource. * @return A ModuleResource object for the given \c path. If the \c path cannot * be found in this module or the module's state is \c UNLOADED, an invalid * ModuleResource object is returned. */ ModuleResource GetResource(const std::string& path) const; /** - * Returns resources in this module and its statically linked modules. + * Returns resources in this module. * * This method is intended to be used to obtain configuration, setup, localization * and other information from this module. * * This method can either return only resources in the specified \c path or recurse * into subdirectories returning resources in the directory tree beginning at the * specified path. * * Examples: * \snippet uServices-resources/main.cpp 0 * - * \note In case of modules statically linked into this module, the returned - * ModuleResource objects can represent the same resource path, coming from - * different static modules. The order of the ModuleResource objects in the - * returned container matches the order of the static module names in the - * #US_LOAD_IMPORTED_MODULES macro. - * * @param path The path name in which to look. The path is always relative to the root * of this module and may begin with '/'. A path value of "/" indicates the root of this module. * @param filePattern The resource name pattern for selecting entries in the specified path. * The pattern is only matched against the last element of the resource path. Substring * matching is supported using the wildcard charachter ('*'). If \c filePattern is empty, * this is equivalent to "*" and matches all resources. * @param recurse If \c true, recurse into subdirectories. Otherwise only return resources * from the specified path. - * @return A vector of ModuleResource objects for each matching entry. The objects are sorted - * such that resources from this module are returned first followed by the resources from - * statically linked modules in the load order as specified in #US_LOAD_IMPORTED_MODULES. + * @return A vector of ModuleResource objects for each matching entry. */ std::vector FindResources(const std::string& path, const std::string& filePattern, bool recurse) const; private: friend class CoreModuleActivator; friend class ModuleRegistry; friend class ServiceReferencePrivate; ModulePrivate* d; Module(); void Init(CoreModuleContext* coreCtx, ModuleInfo* info); void Uninit(); void Start(); void Stop(); // purposely not implemented Module(const Module &); Module& operator=(const Module&); }; US_END_NAMESPACE /** * \ingroup MicroServices */ US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(Module)& module); /** * \ingroup MicroServices */ US_Core_EXPORT std::ostream& operator<<(std::ostream& os, US_PREPEND_NAMESPACE(Module) const * module); #endif // USMODULE_H diff --git a/Core/CppMicroServices/core/include/usModuleActivator.h b/Core/CppMicroServices/core/include/usModuleActivator.h index 903f68ad6c..41691efd28 100644 --- a/Core/CppMicroServices/core/include/usModuleActivator.h +++ b/Core/CppMicroServices/core/include/usModuleActivator.h @@ -1,137 +1,134 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEACTIVATOR_H_ #define USMODULEACTIVATOR_H_ +#ifndef US_MODULE_NAME +#error Missing US_MODULE_NAME preprocessor define +#endif + #include +#include US_BEGIN_NAMESPACE class ModuleContext; /** * \ingroup MicroServices * * Customizes the starting and stopping of a CppMicroServices module. *

* %ModuleActivator is an interface that can be implemented by * CppMicroServices modules. The CppMicroServices library can create instances of a * module's %ModuleActivator as required. If an instance's * ModuleActivator::Load method executes successfully, it is * guaranteed that the same instance's %ModuleActivator::Unload * method will be called when the module is to be unloaded. The CppMicroServices * library does not concurrently call a %ModuleActivator object. * *

* %ModuleActivator is an abstract class interface whose implementations * must be exported via a special macro. Implementations are usually declared * and defined directly in .cpp files. * *

* \snippet uServices-activator/main.cpp 0 * *

* The class implementing the %ModuleActivator interface must have a public * default constructor so that a %ModuleActivator * object can be created by the CppMicroServices library. * */ struct ModuleActivator { virtual ~ModuleActivator() {} /** * Called when this module is loaded. This method * can be used to register services or to allocate any resources that this * module may need globally (during the whole module lifetime). * *

* This method must complete and return to its caller in a timely manner. * * @param context The execution context of the module being loaded. * @throws std::exception If this method throws an exception, this * module is marked as stopped and the framework will remove this * module's listeners, unregister all services registered by this * module, and release all services used by this module. */ virtual void Load(ModuleContext* context) = 0; /** * Called when this module is unloaded. In general, this * method should undo the work that the ModuleActivator::Load * method started. There should be no active threads that were started by * this module when this method returns. * *

* This method must complete and return to its caller in a timely manner. * * @param context The execution context of the module being unloaded. * @throws std::exception If this method throws an exception, the * module is still marked as unloaded, and the framework will remove * the module's listeners, unregister all services registered by the * module, and release all services used by the module. */ virtual void Unload(ModuleContext* context) = 0; }; US_END_NAMESPACE -#define US_MODULE_ACTIVATOR_INSTANCE_FUNCTION(type) \ - struct ScopedPointer \ - { \ - ScopedPointer(US_PREPEND_NAMESPACE(ModuleActivator)* activator = 0) : m_Activator(activator) {} \ - ~ScopedPointer() { delete m_Activator; } \ - US_PREPEND_NAMESPACE(ModuleActivator)* m_Activator; \ - }; \ - \ - static ScopedPointer activatorPtr; \ - if (activatorPtr.m_Activator == 0) activatorPtr.m_Activator = new type; \ - return activatorPtr.m_Activator; /** * \ingroup MicroServices * * \brief Export a module activator class. * - * \param _module_libname The physical name of the module, without prefix or suffix. * \param _activator_type The fully-qualified type-name of the module activator class. * * Call this macro after the definition of your module activator to make it * accessible by the CppMicroServices library. * * Example: * \snippet uServices-activator/main.cpp 0 - * - * \note If you use this macro in a source file compiled into an executable, additional - * requirements for the macro arguments apply: - * - The \c _module_libname argument must match the value of \c _module_name used in the - * \c #US_INITIALIZE_MODULE macro call. */ -#define US_EXPORT_MODULE_ACTIVATOR(_module_libname, _activator_type) \ - extern "C" US_ABI_EXPORT US_PREPEND_NAMESPACE(ModuleActivator)* _us_module_activator_instance_ ## _module_libname () \ - { \ - US_MODULE_ACTIVATOR_INSTANCE_FUNCTION(_activator_type) \ +#define US_EXPORT_MODULE_ACTIVATOR(_activator_type) \ + extern "C" US_ABI_EXPORT US_PREPEND_NAMESPACE(ModuleActivator)* US_CONCAT(_us_module_activator_instance_, US_MODULE_NAME) () \ + { \ + struct ScopedPointer \ + { \ + ScopedPointer(US_PREPEND_NAMESPACE(ModuleActivator)* activator = 0) : m_Activator(activator) {} \ + ~ScopedPointer() { delete m_Activator; } \ + US_PREPEND_NAMESPACE(ModuleActivator)* m_Activator; \ + }; \ + \ + static ScopedPointer activatorPtr; \ + if (activatorPtr.m_Activator == 0) activatorPtr.m_Activator = new _activator_type; \ + return activatorPtr.m_Activator; \ } #endif /* USMODULEACTIVATOR_H_ */ diff --git a/Core/CppMicroServices/core/include/usModuleContext.h b/Core/CppMicroServices/core/include/usModuleContext.h index 5abc94c1cb..19dac422d4 100644 --- a/Core/CppMicroServices/core/include/usModuleContext.h +++ b/Core/CppMicroServices/core/include/usModuleContext.h @@ -1,851 +1,851 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULECONTEXT_H_ #define USMODULECONTEXT_H_ // TODO: Replace includes with forward directives! #include "usListenerFunctors_p.h" #include "usServiceInterface.h" #include "usServiceEvent.h" #include "usServiceRegistration.h" #include "usServiceException.h" #include "usModuleEvent.h" US_BEGIN_NAMESPACE typedef US_SERVICE_LISTENER_FUNCTOR ServiceListener; typedef US_MODULE_LISTENER_FUNCTOR ModuleListener; class ModuleContextPrivate; class ServiceFactory; template class ServiceObjects; /** * \ingroup MicroServices * * A module's execution context within the framework. The context is used to * grant access to other methods so that this module can interact with the * Micro Services Framework. * *

* ModuleContext methods allow a module to: *

    *
  • Subscribe to events published by the framework. *
  • Register service objects with the framework service registry. *
  • Retrieve ServiceReferences from the framework service * registry. *
  • Get and release service objects for a referenced service. *
  • Get the list of modules loaded in the framework. *
  • Get the {@link Module} object for a module. *
* *

* A ModuleContext object will be created and provided to the * module associated with this context when it is loaded using the * {@link ModuleActivator::Load} method. The same ModuleContext * object will be passed to the module associated with this context when it is * unloaded using the {@link ModuleActivator::Unload} method. A * ModuleContext object is generally for the private use of its * associated module and is not meant to be shared with other modules in the * module environment. * *

* The Module object associated with a ModuleContext * object is called the context module. * *

* The ModuleContext object is only valid during the execution of * its context module; that is, during the period when the context module * is loaded. If the ModuleContext * object is used subsequently, a std::logic_error is * thrown. The ModuleContext object is never reused after * its context module is unloaded. * *

* The framework is the only entity that can create ModuleContext * objects. * * @remarks This class is thread safe. */ class US_Core_EXPORT ModuleContext { public: ~ModuleContext(); /** * Returns the Module object associated with this * ModuleContext. This module is called the context module. * * @return The Module object associated with this * ModuleContext. * @throws std::logic_error If this ModuleContext is no * longer valid. */ Module* GetModule() const; /** * Returns the module with the specified identifier. * * @param id The identifier of the module to retrieve. * @return A Module object or 0 if the * identifier does not match any previously loaded module. */ Module* GetModule(long id) const; /** * Get the module that with the specified module name. * * @param name The name of the module to get. * @return The requested \c Module or \c NULL. */ Module* GetModule(const std::string& name); /** * Returns a list of all known modules. *

* This method returns a list of all modules loaded in the module * environment at the time of the call to this method. This list will * also contain modules which might already have been unloaded. * * @return A std::vector of Module objects which * will hold one object per known module. */ std::vector GetModules() const; /** * Registers the specified service object with the specified properties * under the specified class names into the framework. A * ServiceRegistration object is returned. The * ServiceRegistration object is for the private use of the * module registering the service and should not be shared with other * modules. The registering module is defined to be the context module. * Other modules can locate the service by using either the * {@link #GetServiceReferences} or {@link #GetServiceReference} method. * *

* A module can register a service object that implements the * ServiceFactory or PrototypeServiceFactory interface to have more * flexibility in providing service objects to other modules. * *

* The following steps are taken when registering a service: *

    *
  1. The framework adds the following service properties to the service * properties from the specified ServiceProperties (which may be * omitted):
    * A property named ServiceConstants#SERVICE_ID() identifying the * registration number of the service
    * A property named ServiceConstants#OBJECTCLASS() containing all the * specified classes.
    * A property named ServiceConstants#SERVICE_SCOPE() identifying the scope * of the service.
    * Properties with these names in the specified ServiceProperties will * be ignored. *
  2. The service is added to the framework service registry and may now be * used by other modules. *
  3. A service event of type ServiceEvent#REGISTERED is fired. *
  4. A ServiceRegistration object for this registration is * returned. *
* * @note This is a low-level method and should normally not be used directly. * Use one of the templated RegisterService methods instead. * * @param service The service object, which is a map of interface identifiers * to raw service pointers. * @param properties The properties for this service. The keys in the * properties object must all be std::string objects. See * {@link ServiceConstants} for a list of standard service property keys. * Changes should not be made to this object after calling this * method. To update the service's properties the * {@link ServiceRegistration::SetProperties} method must be called. * The set of properties may be omitted if the service has * no properties. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * * @throws std::invalid_argument If one of the following is true: *
    *
  • service is 0. *
  • properties contains case variants of the same key name. *
* @throws std::logic_error If this ModuleContext is no longer valid. * * @see ServiceRegistration * @see ServiceFactory * @see PrototypeServiceFactory */ ServiceRegistrationU RegisterService(const InterfaceMap& service, const ServiceProperties& properties = ServiceProperties()); /** * Registers the specified service object with the specified properties * using the specified template argument with the framework. * *

* This method is provided as a convenience when service will only be registered under * a single class name whose type is available to the caller. It is otherwise identical to * RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred * since it avoids errors in the string literal identifying the class name or interface identifier. * * Example usage: * \snippet uServices-registration/main.cpp 1-1 * \snippet uServices-registration/main.cpp 1-2 * * @tparam S The type under which the service can be located. * @param service The service object or a ServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If the service type \c S is invalid or the * \c service object is NULL. * * @see RegisterService(const InterfaceMap&, const ServiceProperties&) */ template ServiceRegistration RegisterService(S* service, const ServiceProperties& properties = ServiceProperties()) { InterfaceMap servicePointers = MakeInterfaceMap(service); return RegisterService(servicePointers, properties); } /** * Registers the specified service object with the specified properties * using the specified template argument with the framework. * *

* This method is provided as a convenience when registering a service under * two interface classes whose type is available to the caller. It is otherwise identical to * RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred * since it avoids errors in the string literal identifying the class name or interface identifier. * * Example usage: * \snippet uServices-registration/main.cpp 2-1 * \snippet uServices-registration/main.cpp 2-2 * * @tparam I1 The first interface type under which the service can be located. * @tparam I2 The second interface type under which the service can be located. * @param impl The service object or a ServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If the service type \c S is invalid or the * \c service object is NULL. * * @see RegisterService(const InterfaceMap&, const ServiceProperties&) */ template ServiceRegistration RegisterService(Impl* impl, const ServiceProperties& properties = ServiceProperties()) { InterfaceMap servicePointers = MakeInterfaceMap(impl); return RegisterService(servicePointers, properties); } /** * Registers the specified service object with the specified properties * using the specified template argument with the framework. * *

* This method is identical to the RegisterService(Impl*, const ServiceProperties&) * method except that it supports three service interface types. * * @tparam I1 The first interface type under which the service can be located. * @tparam I2 The second interface type under which the service can be located. * @tparam I3 The third interface type under which the service can be located. * @param impl The service object or a ServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If the service type \c S is invalid or the * \c service object is NULL. * * @see RegisterService(const InterfaceMap&, const ServiceProperties&) */ template ServiceRegistration RegisterService(Impl* impl, const ServiceProperties& properties = ServiceProperties()) { InterfaceMap servicePointers = MakeInterfaceMap(impl); return RegisterService(servicePointers, properties); } /** * Registers the specified service factory as a service with the specified properties * using the specified template argument as service interface type with the framework. * *

* This method is provided as a convenience when factory will only be registered under * a single class name whose type is available to the caller. It is otherwise identical to * RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred * since it avoids errors in the string literal identifying the class name or interface identifier. * * Example usage: * \snippet uServices-registration/main.cpp 1-1 * \snippet uServices-registration/main.cpp f1 * * @tparam S The type under which the service can be located. * @param factory The ServiceFactory or PrototypeServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If the service type \c S is invalid or the * \c service factory object is NULL. * * @see RegisterService(const InterfaceMap&, const ServiceProperties&) */ template ServiceRegistration RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties()) { InterfaceMap servicePointers = MakeInterfaceMap(factory); return RegisterService(servicePointers, properties); } /** * Registers the specified service factory as a service with the specified properties * using the specified template argument as service interface type with the framework. * *

* This method is identical to the RegisterService(ServiceFactory*, const ServiceProperties&) * method except that it supports two service interface types. * * Example usage: * \snippet uServices-registration/main.cpp 2-1 * \snippet uServices-registration/main.cpp f2 * * @tparam I1 The first interface type under which the service can be located. * @tparam I2 The second interface type under which the service can be located. * @param factory The ServiceFactory or PrototypeServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If the service type \c S is invalid or the * \c service factory object is NULL. * * @see RegisterService(const InterfaceMap&, const ServiceProperties&) */ template ServiceRegistration RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties()) { InterfaceMap servicePointers = MakeInterfaceMap(factory); return RegisterService(servicePointers, properties); } /** * Registers the specified service factory as a service with the specified properties * using the specified template argument as service interface type with the framework. * *

* This method is identical to the RegisterService(ServiceFactory*, const ServiceProperties&) * method except that it supports three service interface types. * * @tparam I1 The first interface type under which the service can be located. * @tparam I2 The second interface type under which the service can be located. * @tparam I3 The third interface type under which the service can be located. * @param factory The ServiceFactory or PrototypeServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If the service type \c S is invalid or the * \c service factory object is NULL. * * @see RegisterService(const InterfaceMap&, const ServiceProperties&) */ template ServiceRegistration RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties()) { InterfaceMap servicePointers = MakeInterfaceMap(factory); return RegisterService(servicePointers, properties); } /** * Returns a list of ServiceReference objects. The returned * list contains services that * were registered under the specified class and match the specified filter * expression. * *

* The list is valid at the time of the call to this method. However since * the Micro Services framework is a very dynamic environment, services can be modified or * unregistered at any time. * *

* The specified filter expression is used to select the * registered services whose service properties contain keys and values * which satisfy the filter expression. See LDAPFilter for a description * of the filter syntax. If the specified filter is * empty, all registered services are considered to match the * filter. If the specified filter expression cannot be parsed, * an std::invalid_argument will be thrown with a human readable * message where the filter became unparsable. * *

* The result is a list of ServiceReference objects for all * services that meet all of the following conditions: *

    *
  • If the specified class name, clazz, is not * empty, the service must have been registered with the * specified class name. The complete list of class names with which a * service was registered is available from the service's * {@link ServiceConstants#OBJECTCLASS() objectClass} property. *
  • If the specified filter is not empty, the * filter expression must match the service. *
* * @param clazz The class name with which the service was registered or * an empty string for all services. * @param filter The filter expression or empty for all * services. * @return A list of ServiceReference objects or * an empty list if no services are registered which satisfy the * search. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::logic_error If this ModuleContext is no longer valid. */ std::vector GetServiceReferences(const std::string& clazz, const std::string& filter = std::string()); /** * Returns a list of ServiceReference objects. The returned * list contains services that * were registered under the interface id of the template argument S * and match the specified filter expression. * *

* This method is identical to GetServiceReferences(const std::string&, const std::string&) except that * the class name for the service object is automatically deduced from the template argument. * * @tparam S The type under which the requested service objects must have been registered. * @param filter The filter expression or empty for all * services. * @return A list of ServiceReference objects or * an empty list if no services are registered which satisfy the * search. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If the service type \c S is invalid. * * @see GetServiceReferences(const std::string&, const std::string&) */ template std::vector > GetServiceReferences(const std::string& filter = std::string()) { - const char* clazz = us_service_interface_iid(); - if (clazz == 0) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); + std::string clazz = us_service_interface_iid(); + if (clazz.empty()) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); typedef std::vector BaseVectorT; - BaseVectorT serviceRefs = GetServiceReferences(std::string(clazz), filter); + BaseVectorT serviceRefs = GetServiceReferences(clazz, filter); std::vector > result; for(BaseVectorT::const_iterator i = serviceRefs.begin(); i != serviceRefs.end(); ++i) { result.push_back(ServiceReference(*i)); } return result; } /** * Returns a ServiceReference object for a service that * implements and was registered under the specified class. * *

* The returned ServiceReference object is valid at the time of * the call to this method. However as the Micro Services framework is a very dynamic * environment, services can be modified or unregistered at any time. * *

* This method is the same as calling * {@link ModuleContext::GetServiceReferences(const std::string&, const std::string&)} with an * empty filter expression. It is provided as a convenience for * when the caller is interested in any service that implements the * specified class. *

* If multiple such services exist, the service with the highest ranking (as * specified in its ServiceConstants::SERVICE_RANKING() property) is returned. *

* If there is a tie in ranking, the service with the lowest service ID (as * specified in its ServiceConstants::SERVICE_ID() property); that is, the * service that was registered first is returned. * * @param clazz The class name with which the service was registered. * @return A ServiceReference object, or an invalid ServiceReference if * no services are registered which implement the named class. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If no service was registered under the given class name. * * @see #GetServiceReferences(const std::string&, const std::string&) */ ServiceReferenceU GetServiceReference(const std::string& clazz); /** * Returns a ServiceReference object for a service that * implements and was registered under the specified template class argument. * *

* This method is identical to GetServiceReference(const std::string&) except that * the class name for the service object is automatically deduced from the template argument. * * @tparam S The type under which the requested service must have been registered. * @return A ServiceReference object, or an invalid ServiceReference if * no services are registered which implement the type S. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException It no service was registered under the given class name. * @see #GetServiceReference(const std::string&) * @see #GetServiceReferences(const std::string&) */ template ServiceReference GetServiceReference() { - const char* clazz = us_service_interface_iid(); - if (clazz == 0) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); - return ServiceReference(GetServiceReference(std::string(clazz))); + std::string clazz = us_service_interface_iid(); + if (clazz.empty()) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); + return ServiceReference(GetServiceReference(clazz)); } /** * Returns the service object referenced by the specified * ServiceReferenceBase object. *

* A module's use of a service is tracked by the module's use count of that * service. Each time a service's service object is returned by * {@link #GetService(const ServiceReference&)} the context module's use count for * that service is incremented by one. Each time the service is released by * {@link #UngetService(const ServiceReferenceBase&)} the context module's use count * for that service is decremented by one. *

* When a module's use count for a service drops to zero, the module should * no longer use that service. * *

* This method will always return 0 when the service * associated with this reference has been unregistered. * *

* The following steps are taken to get the service object: *

    *
  1. If the service has been unregistered, 0 is returned. *
  2. The context module's use count for this service is incremented by * one. *
  3. If the context module's use count for the service is currently one * and the service was registered with an object implementing the * ServiceFactory interface, the * {@link ServiceFactory::GetService} method is * called to create a service object for the context module. This service * object is cached by the framework. While the context module's use count * for the service is greater than zero, subsequent calls to get the * services's service object for the context module will return the cached * service object.
    * If the ServiceFactory object throws an * exception, 0 is returned and a warning is logged. *
  4. The service object for the service is returned. *
* * @param reference A reference to the service. * @return A service object for the service associated with * reference or 0 if the service is not * registered or the ServiceFactory threw * an exception. * @throws std::logic_error If this ModuleContext is no * longer valid. * @throws std::invalid_argument If the specified * ServiceReferenceBase is invalid (default constructed). * @see #UngetService(const ServiceReferenceBase&) * @see ServiceFactory */ void* GetService(const ServiceReferenceBase& reference); InterfaceMap GetService(const ServiceReferenceU& reference); /** * Returns the service object referenced by the specified * ServiceReference object. *

* This is a convenience method which is identical to void* GetService(const ServiceReferenceBase&) * except that it casts the service object to the supplied template argument type * * @tparam S The type the service object will be cast to. * @return A service object for the service associated with * reference or 0 if the service is not * registered, the ServiceFactory threw * an exception or the service could not be casted to the desired type. * @throws std::logic_error If this ModuleContext is no * longer valid. * @throws std::invalid_argument If the specified * ServiceReference is invalid (default constructed). * @see #GetService(const ServiceReferenceBase&) * @see #UngetService(const ServiceReferenceBase&) * @see ServiceFactory */ template S* GetService(const ServiceReference& reference) { const ServiceReferenceBase& baseRef = reference; return reinterpret_cast(GetService(baseRef)); } /** * Returns the ServiceObjects object for the service referenced by the specified * ServiceReference object. The ServiceObjects object can be used to obtain * multiple service objects for services with prototype scope. For services with * singleton or module scope, the ServiceObjects::GetService() method behaves * the same as the GetService(const ServiceReference&) method and the * ServiceObjects::UngetService(const ServiceReferenceBase&) method behaves the * same as the UngetService(const ServiceReferenceBase&) method. That is, only one, * use-counted service object is available from the ServiceObjects object. * * @tparam S Type of Service. * @param reference A reference to the service. * @return A ServiceObjects object for the service associated with the specified * reference or an invalid instance if the service is not registered. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws std::invalid_argument If the specified ServiceReference is invalid * (default constructed or the service has been unregistered) * * @see PrototypeServiceFactory */ template ServiceObjects GetServiceObjects(const ServiceReference& reference) { return ServiceObjects(this, reference); } /** * Releases the service object referenced by the specified * ServiceReference object. If the context module's use count * for the service is zero, this method returns false. * Otherwise, the context modules's use count for the service is decremented * by one. * *

* The service's service object should no longer be used and all references * to it should be destroyed when a module's use count for the service drops * to zero. * *

* The following steps are taken to unget the service object: *

    *
  1. If the context module's use count for the service is zero or the * service has been unregistered, false is returned. *
  2. The context module's use count for this service is decremented by * one. *
  3. If the context module's use count for the service is currently zero * and the service was registered with a ServiceFactory object, * the ServiceFactory#UngetService * method is called to release the service object for the context module. *
  4. true is returned. *
* * @param reference A reference to the service to be released. * @return false if the context module's use count for the * service is zero or if the service has been unregistered; * true otherwise. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see #GetService * @see ServiceFactory */ bool UngetService(const ServiceReferenceBase& reference); void AddServiceListener(const ServiceListener& delegate, const std::string& filter = std::string()); void RemoveServiceListener(const ServiceListener& delegate); void AddModuleListener(const ModuleListener& delegate); void RemoveModuleListener(const ModuleListener& delegate); /** * Adds the specified callback with the * specified filter to the context modules's list of listeners. * See LDAPFilter for a description of the filter syntax. Listeners * are notified when a service has a lifecycle state change. * *

* You must take care to remove registered listeners befor the receiver * object is destroyed. However, the Micro Services framework takes care * of removing all listeners registered by this context module's classes * after the module is unloaded. * *

* If the context module's list of listeners already contains a pair (r,c) * of receiver and callback such that * (r == receiver && c == callback), then this * method replaces that callback's filter (which may be empty) * with the specified one (which may be empty). * *

* The callback is called if the filter criteria is met. To filter based * upon the class of the service, the filter should reference the * ServiceConstants#OBJECTCLASS() property. If filter is * empty, all services are considered to match the filter. * *

* When using a filter, it is possible that the * ServiceEvents for the complete lifecycle of a service * will not be delivered to the callback. For example, if the * filter only matches when the property x has * the value 1, the callback will not be called if the * service is registered with the property x not set to the * value 1. Subsequently, when the service is modified * setting property x to the value 1, the * filter will match and the callback will be called with a * ServiceEvent of type MODIFIED. Thus, the * callback will not be called with a ServiceEvent of type * REGISTERED. * * @tparam R The type of the receiver (containing the member function to be called) * @param receiver The object to connect to. * @param callback The member function pointer to call. * @param filter The filter criteria. * @throws std::invalid_argument If filter contains an * invalid filter string that cannot be parsed. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see ServiceEvent * @see RemoveServiceListener() */ template void AddServiceListener(R* receiver, void(R::*callback)(const ServiceEvent), const std::string& filter = std::string()) { AddServiceListener(ServiceListenerMemberFunctor(receiver, callback), static_cast(receiver), filter); } /** * Removes the specified callback from the context module's * list of listeners. * *

* If the (receiver,callback) pair is not contained in this * context module's list of listeners, this method does nothing. * * @tparam R The type of the receiver (containing the member function to be removed) * @param receiver The object from which to disconnect. * @param callback The member function pointer to remove. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see AddServiceListener() */ template void RemoveServiceListener(R* receiver, void(R::*callback)(const ServiceEvent)) { RemoveServiceListener(ServiceListenerMemberFunctor(receiver, callback), static_cast(receiver)); } /** * Adds the specified callback to the context modules's list * of listeners. Listeners are notified when a module has a lifecycle * state change. * *

* If the context module's list of listeners already contains a pair (r,c) * of receiver and callback such that * (r == receiver && c == callback), then this method does nothing. * * @tparam R The type of the receiver (containing the member function to be called) * @param receiver The object to connect to. * @param callback The member function pointer to call. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see ModuleEvent */ template void AddModuleListener(R* receiver, void(R::*callback)(const ModuleEvent)) { AddModuleListener(ModuleListenerMemberFunctor(receiver, callback), static_cast(receiver)); } /** * Removes the specified callback from the context module's * list of listeners. * *

* If the (receiver,callback) pair is not contained in this * context module's list of listeners, this method does nothing. * * @tparam R The type of the receiver (containing the member function to be removed) * @param receiver The object from which to disconnect. * @param callback The member function pointer to remove. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see AddModuleListener() */ template void RemoveModuleListener(R* receiver, void(R::*callback)(const ModuleEvent)) { RemoveModuleListener(ModuleListenerMemberFunctor(receiver, callback), static_cast(receiver)); } /** * Get the absolute path for a file or directory in the persistent * storage area provided for the module. The returned path * might be empty if no storage path has been set previously. * If the path is non-empty, it is safe to assume that the path is writable. * * @see ModuleSettings::SetStoragePath(const std::string&) * * @param filename A relative name to the file or directory to be accessed. * @return The absolute path to the persistent storage area for the given file name. */ std::string GetDataFile(const std::string& filename) const; private: friend class Module; friend class ModulePrivate; ModuleContext(ModulePrivate* module); // purposely not implemented ModuleContext(const ModuleContext&); ModuleContext& operator=(const ModuleContext&); void AddServiceListener(const ServiceListener& delegate, void* data, const std::string& filter); void RemoveServiceListener(const ServiceListener& delegate, void* data); void AddModuleListener(const ModuleListener& delegate, void* data); void RemoveModuleListener(const ModuleListener& delegate, void* data); ModuleContextPrivate * const d; }; US_END_NAMESPACE #endif /* USMODULECONTEXT_H_ */ diff --git a/Core/CppMicroServices/core/include/usModuleEvent.h b/Core/CppMicroServices/core/include/usModuleEvent.h index 7394c5a4cb..dcaa2172dd 100644 --- a/Core/CppMicroServices/core/include/usModuleEvent.h +++ b/Core/CppMicroServices/core/include/usModuleEvent.h @@ -1,162 +1,162 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEEVENT_H #define USMODULEEVENT_H #include #include "usSharedData.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4251) #endif US_BEGIN_NAMESPACE class Module; class ModuleEventData; /** * \ingroup MicroServices * * An event from the Micro Services framework describing a module lifecycle change. *

* ModuleEvent objects are delivered to listeners connected * via ModuleContext::AddModuleListener() when a change * occurs in a modules's lifecycle. A type code is used to identify * the event type for future extendability. * * @see ModuleContext#AddModuleListener */ class US_Core_EXPORT ModuleEvent { SharedDataPointer d; public: enum Type { /** * The module has been loaded. *

* The module's * \link ModuleActivator::Load(ModuleContext*) ModuleActivator Load\endlink method * has been executed. */ LOADED, /** * The module has been unloaded. *

* The module's * \link ModuleActivator::Unload(ModuleContext*) ModuleActivator Unload\endlink method * has been executed. */ UNLOADED, /** * The module is about to be loaded. *

* The module's * \link ModuleActivator::Load(ModuleContext*) ModuleActivator Load\endlink method * is about to be called. */ LOADING, /** * The module is about to be unloaded. *

* The module's * \link ModuleActivator::Unload(ModuleContext*) ModuleActivator Unload\endlink method * is about to be called. */ UNLOADING }; /** * Creates an invalid instance. */ ModuleEvent(); ~ModuleEvent(); /** * Can be used to check if this ModuleEvent instance is valid, * or if it has been constructed using the default constructor. * * @return true if this event object is valid, * false otherwise. */ bool IsNull() const; /** * Creates a module event of the specified type. * * @param type The event type. * @param module The module which had a lifecycle change. */ ModuleEvent(Type type, Module* module); ModuleEvent(const ModuleEvent& other); ModuleEvent& operator=(const ModuleEvent& other); /** * Returns the module which had a lifecycle change. * * @return The module that had a change occur in its lifecycle. */ Module* GetModule() const; /** * Returns the type of lifecyle event. The type values are: *

    *
  • {@link #LOADING} *
  • {@link #LOADED} *
  • {@link #UNLOADING} *
  • {@link #UNLOADED} *
* * @return The type of lifecycle event. */ Type GetType() const; }; -US_END_NAMESPACE - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - /** * \ingroup MicroServices * @{ */ -US_Core_EXPORT std::ostream& operator<<(std::ostream& os, US_PREPEND_NAMESPACE(ModuleEvent::Type) eventType); -US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ModuleEvent)& event); +US_Core_EXPORT std::ostream& operator<<(std::ostream& os, ModuleEvent::Type eventType); +US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ModuleEvent& event); /** @}*/ +US_END_NAMESPACE + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #endif // USMODULEEVENT_H diff --git a/Core/CppMicroServices/core/include/usModuleEventHook.h b/Core/CppMicroServices/core/include/usModuleEventHook.h index b1b8898054..e86fd4b369 100644 --- a/Core/CppMicroServices/core/include/usModuleEventHook.h +++ b/Core/CppMicroServices/core/include/usModuleEventHook.h @@ -1,72 +1,70 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEEVENTHOOK_H #define USMODULEEVENTHOOK_H #include "usServiceInterface.h" #include "usShrinkableVector.h" US_BEGIN_NAMESPACE class ModuleContext; class ModuleEvent; /** * @ingroup MicroServices * * %Module Event Hook Service. * *

* Modules registering this service will be called during module lifecycle * (loading, loaded, unloading, and unloaded) operations. * * @remarks Implementations of this interface are required to be thread-safe. */ struct US_Core_EXPORT ModuleEventHook { virtual ~ModuleEventHook(); /** * Module event hook method. This method is called prior to module event * delivery when a module is loading, loaded, unloading, or unloaded. * This method can filter the modules which receive the event. *

* This method is called one and only one time for * each module event generated, this includes module events which are * generated when there are no module listeners registered. * * @param event The module event to be delivered. * @param contexts A list of Module Contexts for modules which have * listeners to which the specified event will be delivered. The * implementation of this method may remove module contexts from the * list to prevent the event from being delivered to the * associated modules. */ virtual void Event(const ModuleEvent& event, ShrinkableVector& contexts) = 0; }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(us::ModuleEventHook, "org.cppmicroservices.ModuleEventHook/2.0.0") - #endif // USMODULEEVENTHOOK_H diff --git a/Core/CppMicroServices/core/include/usModuleFindHook.h b/Core/CppMicroServices/core/include/usModuleFindHook.h index 6d02b13a0f..ff02e1d699 100644 --- a/Core/CppMicroServices/core/include/usModuleFindHook.h +++ b/Core/CppMicroServices/core/include/usModuleFindHook.h @@ -1,73 +1,71 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEFINDHOOK_H #define USMODULEFINDHOOK_H #include "usServiceInterface.h" #include "usShrinkableVector.h" US_BEGIN_NAMESPACE class Module; class ModuleContext; /** * @ingroup MicroServices * * %Module Context Hook Service. * *

* Modules registering this service will be called during module find * (get modules) operations. * * @remarks Implementations of this interface are required to be thread-safe. */ struct US_Core_EXPORT ModuleFindHook { virtual ~ModuleFindHook(); /** * Find hook method. This method is called for module find operations * using ModuleContext::GetBundle(long) * and ModuleContext::GetModules() methods. The find method can * filter the result of the find operation. * * \note A find operation using the ModuleContext::GetModule(const std::string&) * method does not cause the find method to be called, neither does any * call to the static methods of the ModuleRegistry class. * * @param context The module context of the module performing the find * operation. * @param modules A list of Modules to be returned as a result of the * find operation. The implementation of this method may remove * modules from the list to prevent the modules from being * returned to the module performing the find operation. */ virtual void Find(const ModuleContext* context, ShrinkableVector& modules) = 0; }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(us::ModuleFindHook, "org.cppmicroservices.ModuleFindHook/2.0.0") - #endif // USMODULEFINDHOOK_H diff --git a/Core/CppMicroServices/core/include/usModuleImport.h b/Core/CppMicroServices/core/include/usModuleImport.h index c10de0136a..d72acf0503 100644 --- a/Core/CppMicroServices/core/include/usModuleImport.h +++ b/Core/CppMicroServices/core/include/usModuleImport.h @@ -1,158 +1,93 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEIMPORT_H #define USMODULEIMPORT_H #include #include US_BEGIN_NAMESPACE struct ModuleActivator; US_END_NAMESPACE /** * \ingroup MicroServices * - * \brief Import a static module. + * \brief Initialize a static module. * - * \param _import_module_libname The physical name of the module to import, without prefix or suffix. + * \param _module_name The name of the module to initialize. * - * This macro imports the static module named \c _import_module_libname. + * This macro initializes the static module named \c _module_name. * - * Inserting this macro into your application's source code will allow you to make use of - * a static module. Do not forget to list the imported module when calling the macro - * #US_LOAD_IMPORTED_MODULES and to actually link the static module to the importing - * executable or shared library. - * - * Example: - * \snippet uServices-staticmodules/main.cpp ImportStaticModuleIntoLib - * - * \sa US_LOAD_IMPORTED_MODULES - * \sa \ref MicroServices_StaticModules - */ -#define US_IMPORT_MODULE(_import_module_libname) \ - extern "C" US_PREPEND_NAMESPACE(ModuleActivator)* _us_module_activator_instance_ ## _import_module_libname (); \ - void _dummy_reference_to_ ## _import_module_libname ## _activator() \ - { \ - _us_module_activator_instance_ ## _import_module_libname(); \ - } - -/** - * \ingroup MicroServices - * - * \brief Import a static module's resources. - * - * \param _import_module_libname The physical name of the module to import, without prefix or suffix. - * - * This macro imports the resources of the static module named \c _import_module_libname. - * - * Inserting this macro into your application's source code will allow you to make use of - * the resources embedded in a static module. Do not forget to list the imported module when - * calling the macro #US_LOAD_IMPORTED_MODULES and to actually link the static module to the - * importing executable or shared library. + * It the module provides an activator, use the #US_IMPORT_MODULE macro instead, + * to ensure that the activator is called. Do not forget to actually link + * the static module to the importing executable or shared library. * * \sa US_IMPORT_MODULE - * \sa US_LOAD_IMPORTED_MODULES + * \sa US_IMPORT_MODULE_RESOURCES * \sa \ref MicroServices_StaticModules */ -#define US_IMPORT_MODULE_RESOURCES(_import_module_libname) \ - extern "C" US_PREPEND_NAMESPACE(ModuleActivator)* _us_init_resources_ ## _import_module_libname (); \ - void _dummy_reference_to_ ## _import_module_libname ## _init_resources() \ +#define US_INITIALIZE_STATIC_MODULE(_module_name) \ + extern "C" void _us_import_module_initializer_ ## _module_name(); \ + struct StaticModuleInitializer_ ## _module_name \ { \ - _us_init_resources_ ## _import_module_libname(); \ - } + StaticModuleInitializer_ ## _module_name() \ + { \ + _us_import_module_initializer_ ## _module_name(); \ + } \ + }; \ + static StaticModuleInitializer_ ## _module_name _InitializeModule_ ## _module_name; /** * \ingroup MicroServices - * \def US_LOAD_IMPORTED_MODULES_INTO_MAIN(_static_modules) * - * \brief Import a list of static modules into an executable. + * \brief Import a static module. * - * \param _static_modules A space-deliminated list of physical module names, without prefix - * or suffix. + * \param _module_name The name of the module to import. * - * This macro ensures that the ModuleActivator::Load(ModuleContext*) function is called - * for each imported static module when the importing executable is loaded (if the static - * module provides an activator). If the static module provides embedded resources and - * the US_IMPORT_MODULE_RESOURCES macro was called, the resources will be made available - * through the importing module. + * This macro imports the static module named \c _module_name. * - * There must be exactly one call of this macro in the executable which is - * importing static modules. + * Inserting this macro into your application's source code will allow you to make use of + * a static module. It will initialize the static module and calls its + * ModuleActivator. It the module does not provide an activator, use the + * #US_INITIALIZE_STATIC_MODULE macro instead. Do not forget to actually link + * the static module to the importing executable or shared library. * * Example: * \snippet uServices-staticmodules/main.cpp ImportStaticModuleIntoMain * - * \sa US_IMPORT_MODULE - * \sa US_LOAD_IMPORTED_MODULES - * \sa \ref MicroServices_StaticModules - */ - -#ifdef US_BUILD_SHARED_LIBS -#define US_LOAD_IMPORTED_MODULES_INTO_MAIN(_static_modules) \ - extern "C" US_ABI_EXPORT const char* _us_get_imported_modules_for_() \ - { \ - return #_static_modules; \ - } -#else -#define US_LOAD_IMPORTED_MODULES_INTO_MAIN(_static_modules) \ - extern "C" US_ABI_EXPORT const char* _us_get_imported_modules_for_CppMicroServices() \ - { \ - return #_static_modules; \ - } -#endif - -/** - * \ingroup MicroServices - * - * \brief Import a list of static modules into a shared library. - * - * \param _module_libname The physical name of the importing module, without prefix or suffix. - * \param _static_modules A space-deliminated list of physical module names, without prefix - * or suffix. - * - * This macro ensures that the ModuleActivator::Load(ModuleContext*) function is called - * for each imported static module when the importing shared library is loaded (if the static - * module provides an activator). If the static module provides embedded resources and - * the US_IMPORT_MODULE_RESOURCES macro was called, the resources will be made available - * through the importing module. - * - * There must be exactly one call of this macro in the shared library which is - * importing static modules. - * - * Example: - * \snippet uServices-staticmodules/main.cpp ImportStaticModuleIntoLib - * - * \sa US_IMPORT_MODULE - * \sa US_LOAD_IMPORTED_MODULES_INTO_MAIN + * \sa US_INITIALIZE_STATIC_MODULE + * \sa US_IMPORT_MODULE_RESOURCES * \sa \ref MicroServices_StaticModules */ -#define US_LOAD_IMPORTED_MODULES(_module_libname, _static_modules) \ - extern "C" US_ABI_EXPORT const char* _us_get_imported_modules_for_ ## _module_libname () \ - { \ - return #_static_modules; \ +#define US_IMPORT_MODULE(_module_name) \ + US_INITIALIZE_STATIC_MODULE(_module_name) \ + extern "C" US_PREPEND_NAMESPACE(ModuleActivator)* _us_module_activator_instance_ ## _module_name (); \ + void _dummy_reference_to_ ## _module_name ## _activator() \ + { \ + _us_module_activator_instance_ ## _module_name(); \ } #endif // USMODULEREGISTRY_H diff --git a/Core/CppMicroServices/core/include/usModuleInfo.h b/Core/CppMicroServices/core/include/usModuleInfo.h index 0ca559bb45..e3ff629e56 100644 --- a/Core/CppMicroServices/core/include/usModuleInfo.h +++ b/Core/CppMicroServices/core/include/usModuleInfo.h @@ -1,77 +1,59 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEINFO_H #define USMODULEINFO_H #include #include -#include #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4251) #endif US_BEGIN_NAMESPACE struct ModuleActivator; /** * This class is not intended to be used directly. It is exported to support * the CppMicroServices module system. */ struct US_Core_EXPORT ModuleInfo { - ModuleInfo(const std::string& name, const std::string& libName); - - typedef ModuleActivator*(*ModuleActivatorHook)(void); - typedef int(*InitResourcesHook)(ModuleInfo*); - typedef const unsigned char* ModuleResourceData; + ModuleInfo(const std::string& name); std::string name; - std::string libName; - std::string location; - std::string autoLoadDir; - long id; - - ModuleActivatorHook activatorHook; - - // In case of statically linked (imported) modules, there could - // be more than one set of ModuleResourceData pointers. We aggregate - // all pointers here. - std::vector resourceData; - std::vector resourceNames; - std::vector resourceTree; }; US_END_NAMESPACE #ifdef _MSC_VER # pragma warning(pop) #endif #endif // USMODULEINFO_H diff --git a/Core/CppMicroServices/core/include/usModuleInitialization.h b/Core/CppMicroServices/core/include/usModuleInitialization.h index 9e0bc294ad..66f1fe9b3b 100644 --- a/Core/CppMicroServices/core/include/usModuleInitialization.h +++ b/Core/CppMicroServices/core/include/usModuleInitialization.h @@ -1,174 +1,120 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ +#ifndef US_MODULE_NAME +#error Missing US_MODULE_NAME preprocessor define +#endif + +#ifndef USMODULEINITIALIZATION_H +#define USMODULEINITIALIZATION_H + #include #include #include -#include -#include #include #include -#ifndef USMODULEINITIALIZATION_H -#define USMODULEINITIALIZATION_H /** * \ingroup MicroServices * * \brief Creates initialization code for a module. * * Each module which wants to register itself with the CppMicroServices library - * has to put a call to this macro in one of its source files. - * - * Example call for a module with file-name "libmylibname.so": - * \code - * US_INITIALIZE_MODULE("My Service Implementation", "mylibname") - * \endcode + * has to put a call to this macro in one of its source files. Further, the modules + * source files must be compiled with the \c US_MODULE_NAME pre-processor definition + * set to a module-unique identifier. * - * This will initialize the module for use with the CppMicroServices library, using a default - * auto-load directory named after the provided library name in \c _module_libname. + * Calling the \c US_INITIALIZE_MODULE macro will initialize the module for use with + * the CppMicroServices library, using a default auto-load directory named after the + * \c US_MODULE_NAME definition. * * \sa MicroServices_AutoLoading * * \remarks If you are using CMake, consider using the provided CMake macro * usFunctionGenerateModuleInit(). - * - * \param _module_name A human-readable name for the module. - * If you use this macro in a source file for an executable, the module name must - * be a valid C-identifier (no spaces etc.). - * \param _module_libname The physical name of the module, withou prefix or suffix. - * - * \note If you want to create initialization code for an executable, see - * #US_INITIALIZE_EXECUTABLE. */ -#define US_INITIALIZE_MODULE(_module_name, _module_libname) \ +#define US_INITIALIZE_MODULE \ US_BEGIN_NAMESPACE \ +namespace { \ \ /* Declare a file scoped ModuleInfo object */ \ -US_GLOBAL_STATIC_WITH_ARGS(ModuleInfo, moduleInfo, (_module_name, _module_libname)) \ +US_GLOBAL_STATIC_WITH_ARGS(ModuleInfo, moduleInfo, (US_STR(US_MODULE_NAME))) \ \ /* This class is used to statically initialize the library within the C++ Micro services \ library. It looks up a library specific C-style function returning an instance \ of the ModuleActivator interface. */ \ -class US_ABI_LOCAL ModuleInitializer { \ +class US_ABI_LOCAL US_CONCAT(ModuleInitializer_, US_MODULE_NAME) { \ \ public: \ \ - ModuleInitializer() \ + US_CONCAT(ModuleInitializer_, US_MODULE_NAME)() \ { \ ModuleInfo*(*moduleInfoPtr)() = moduleInfo; \ void* moduleInfoSym = NULL; \ std::memcpy(&moduleInfoSym, &moduleInfoPtr, sizeof(void*)); \ - std::string location = ModuleUtils::GetLibraryPath(moduleInfo()->libName, moduleInfoSym); \ - std::string activator_func = "_us_module_activator_instance_"; \ - if(moduleInfo()->libName.empty()) \ - { \ - activator_func.append(moduleInfo()->name); \ - } \ - else \ - { \ - activator_func.append(moduleInfo()->libName); \ - } \ - \ - moduleInfo()->location = location; \ - \ - if (moduleInfo()->libName.empty()) \ - { \ - /* make sure we retrieve symbols from the executable, if "libName" is empty */ \ - location.clear(); \ - } \ - void* activatorHookSym = ModuleUtils::GetSymbol(location, activator_func.c_str()); \ - std::memcpy(&moduleInfo()->activatorHook, &activatorHookSym, sizeof(void*)); \ + std::string location = ModuleUtils::GetLibraryPath(moduleInfoSym); \ + moduleInfoPtr()->location = location; \ \ Register(); \ } \ \ static void Register() \ { \ ModuleRegistry::Register(moduleInfo()); \ } \ \ - ~ModuleInitializer() \ + ~US_CONCAT(ModuleInitializer_, US_MODULE_NAME)() \ { \ ModuleRegistry::UnRegister(moduleInfo()); \ } \ \ }; \ \ -US_ABI_LOCAL ModuleContext* GetModuleContext() \ -{ \ - /* make sure the module is registered */ \ - if (moduleInfo()->id == 0) \ - { \ - ModuleInitializer::Register(); \ - } \ \ - return ModuleRegistry::GetModule(moduleInfo()->id)->GetModuleContext(); \ +US_DEFINE_MODULE_INITIALIZER \ } \ \ US_END_NAMESPACE \ \ -static US_PREPEND_NAMESPACE(ModuleInitializer) _InitializeModule; +/* A helper function which is called by the US_IMPORT_MODULE macro to initialize \ + static modules */ \ +extern "C" void US_ABI_LOCAL US_CONCAT(_us_import_module_initializer_, US_MODULE_NAME)() \ +{ \ + static US_PREPEND_NAMESPACE(US_CONCAT(ModuleInitializer_, US_MODULE_NAME)) US_CONCAT(_InitializeModule_, US_MODULE_NAME); \ +} -/** - * \ingroup MicroServices - * - * \brief Creates initialization code for an executable. - * - * Each executable which wants to register itself with the CppMicroServices library - * has to put a call to this macro in one of its source files. This ensures that the - * executable will get its own ModuleContext instance and can access the service registry. - * - * Example call for an executable: - * \code - * US_INITIALIZE_EXECUTABLE("my_executable") - * \endcode - * - * This will initialize the executable for use with the CppMicroServices library, using a default - * auto-load directory named after the provided executable id in \c _executable_id. - * - * \sa MicroServices_AutoLoading - * - * \remarks If you are using CMake, consider using the provided CMake macro - * usFunctionGenerateExecutableInit(). - * - * \param _executable_id A valid C identifier for the executable (no spaces etc.). - */ -#define US_INITIALIZE_EXECUTABLE(_executable_id) \ - US_INITIALIZE_MODULE(_executable_id, "") - -// If the CppMicroServices library was statically build, executables will share the -// initialization code with the CppMicroServices library. -#ifndef US_BUILD_SHARED_LIBS -#undef US_INITIALIZE_EXECUTABLE -#define US_INITIALIZE_EXECUTABLE(_a) -#endif +// Create a file-scoped static object for registering the module +// during static initialization of the shared library +#define US_DEFINE_MODULE_INITIALIZER \ +static US_CONCAT(ModuleInitializer_, US_MODULE_NAME) US_CONCAT(_InitializeModule_, US_MODULE_NAME); -// Static modules usually don't get initialization code. They are initialized within the -// module importing the static module(s). -#if defined(US_STATIC_MODULE) && !defined(US_FORCE_MODULE_INIT) -#undef US_INITIALIZE_MODULE -#define US_INITIALIZE_MODULE(_a,_b) +// Static modules don't create a file-scoped static object for initialization +// (it would be discarded during static linking anyway). The initialization code +// is triggered by the US_IMPORT_MODULE macro instead. +#if defined(US_STATIC_MODULE) +#undef US_DEFINE_MODULE_INITIALIZER +#define US_DEFINE_MODULE_INITIALIZER #endif #endif // USMODULEINITIALIZATION_H diff --git a/Core/CppMicroServices/core/include/usModuleRegistry.h b/Core/CppMicroServices/core/include/usModuleRegistry.h index 58a7e37a62..3ac2176804 100644 --- a/Core/CppMicroServices/core/include/usModuleRegistry.h +++ b/Core/CppMicroServices/core/include/usModuleRegistry.h @@ -1,92 +1,90 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEREGISTRY_H #define USMODULEREGISTRY_H #include #include #include US_BEGIN_NAMESPACE class Module; struct ModuleInfo; struct ModuleActivator; /** * \ingroup MicroServices * * Here we handle all the modules that are loaded in the framework. */ class US_Core_EXPORT ModuleRegistry { public: /** * Get the module that has the specified module identifier. * * @param id The identifier of the module to get. * @return Module or null * if the module was not found. */ static Module* GetModule(long id); /** * Get the module that has specified module name. * * @param name The name of the module to get. * @return Module or null. */ static Module* GetModule(const std::string& name); /** * Get all known modules. * * @return A list which is filled with all known modules. */ static std::vector GetModules(); /** * Get all modules currently in module state LOADED. * * @return A list which is filled with all modules in * state LOADED */ static std::vector GetLoadedModules(); -private: + static void Register(ModuleInfo* info); - friend class ModuleInitializer; + static void UnRegister(const ModuleInfo* info); + +private: // disabled ModuleRegistry(); - static void Register(ModuleInfo* info); - - static void UnRegister(const ModuleInfo* info); - }; US_END_NAMESPACE #endif // USMODULEREGISTRY_H diff --git a/Core/CppMicroServices/core/include/usModuleResource.h b/Core/CppMicroServices/core/include/usModuleResource.h index c56f8138d0..b76a5bfd0b 100644 --- a/Core/CppMicroServices/core/include/usModuleResource.h +++ b/Core/CppMicroServices/core/include/usModuleResource.h @@ -1,309 +1,312 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULERESOURCE_H #define USMODULERESOURCE_H #include #include #include US_MSVC_PUSH_DISABLE_WARNING(4396) US_BEGIN_NAMESPACE class ModuleResourcePrivate; -class ModuleResourceTree; +class ModuleResourceContainer; /** * \ingroup MicroServices * * Represents a resource (text file, image, etc.) embedded in a CppMicroServices module. * * A \c %ModuleResource object provides information about a resource (external file) which * was embedded into this module's shared library. \c %ModuleResource objects can be obtained * be calling Module#GetResource or Module#FindResources. * * Example code for retreiving a resource object and reading its contents: * \snippet uServices-resources/main.cpp 1 * * %ModuleResource objects have value semantics and copies are very inexpensive. * * \see ModuleResourceStream * \see \ref MicroServices_Resources */ class US_Core_EXPORT ModuleResource { private: typedef ModuleResourcePrivate* ModuleResource::*bool_type; public: /** * Creates in invalid %ModuleResource object. */ ModuleResource(); /** * Copy constructor. * @param resource The object to be copied. */ ModuleResource(const ModuleResource& resource); ~ModuleResource(); /** * Assignment operator. * * @param resource The %ModuleResource object which is assigned to this instance. * @return A reference to this %ModuleResource instance. */ ModuleResource& operator=(const ModuleResource& resource); /** * A less then operator using the full resource path as returned by * GetResourcePath() to define the ordering. * * @param resource The object to which this %ModuleResource object is compared to. * @return \c true if this %ModuleResource object is less then \c resource, * \c false otherwise. */ bool operator<(const ModuleResource& resource) const; /** * Equality operator for %ModuleResource objects. * * @param resource The object for testing equality. * @return \c true if this %ModuleResource object is equal to \c resource, i.e. * they are coming from the same module (shared or static) and have an equal * resource path, \c false otherwise. */ bool operator==(const ModuleResource& resource) const; /** * Inequality operator for %ModuleResource objects. * * @param resource The object for testing inequality. * @return The result of !(*this == resource). */ bool operator!=(const ModuleResource& resource) const; /** * Tests this %ModuleResource object for validity. * * Invalid %ModuleResource objects are created by the default constructor or - * can be returned by the Module class if the resource path is not found. If a - * module from which %ModuleResource objects have been obtained is un-loaded, - * these objects become invalid. + * can be returned by the Module class if the resource path is not found. * * @return \c true if this %ModuleReource object is valid and can safely be used, * \c false otherwise. */ bool IsValid() const; - /** - * Returns \c true if the resource represents a file and the resource data - * is in a compressed format, \c false otherwise. - * - * @return \c true if the resource data is compressed, \c false otherwise. - */ - bool IsCompressed() const; - /** * Boolean conversion operator using IsValid(). */ operator bool_type() const; /** * Returns the name of the resource, excluding the path. * * Example: * \code * ModuleResource resource = module->GetResource("/data/archive.tar.gz"); * std::string name = resource.GetName(); // name = "archive.tar.gz" * \endcode * * @return The resource name. * @see GetPath(), GetResourcePath() */ std::string GetName() const; /** * Returns the resource's path, without the file name. * * Example: * \code * ModuleResource resource = module->GetResource("/data/archive.tar.gz"); - * std::string path = resource.GetPath(); // path = "/data" + * std::string path = resource.GetPath(); // path = "/data/" * \endcode * + * The path with always begin and end with a forward slash. + * * @return The resource path without the name. * @see GetResourcePath(), GetName() and IsDir() */ std::string GetPath() const; /** * Returns the resource path including the file name. * * @return The resource path including the file name. * @see GetPath(), GetName() and IsDir() */ std::string GetResourcePath() const; /** * Returns the base name of the resource without the path. * * Example: * \code * ModuleResource resource = module->GetResource("/data/archive.tar.gz"); * std::string base = resource.GetBaseName(); // base = "archive" * \endcode * * @return The resource base name. * @see GetName(), GetSuffix(), GetCompleteSuffix() and GetCompleteBaseName() */ std::string GetBaseName() const; /** * Returns the complete base name of the resource without the path. * * Example: * \code * ModuleResource resource = module->GetResource("/data/archive.tar.gz"); * std::string base = resource.GetCompleteBaseName(); // base = "archive.tar" * \endcode * * @return The resource's complete base name. * @see GetName(), GetSuffix(), GetCompleteSuffix(), and GetBaseName() */ std::string GetCompleteBaseName() const; /** * Returns the suffix of the resource. * * The suffix consists of all characters in the resource name after (but not * including) the last '.'. * * Example: * \code * ModuleResource resource = module->GetResource("/data/archive.tar.gz"); * std::string suffix = resource.GetSuffix(); // suffix = "gz" * \endcode * * @return The resource name suffix. * @see GetName(), GetCompleteSuffix(), GetBaseName() and GetCompleteBaseName() */ std::string GetSuffix() const; /** * Returns the complete suffix of the resource. * * The suffix consists of all characters in the resource name after (but not * including) the first '.'. * * Example: * \code * ModuleResource resource = module->GetResource("/data/archive.tar.gz"); * std::string suffix = resource.GetCompleteSuffix(); // suffix = "tar.gz" * \endcode * * @return The resource name suffix. * @see GetName(), GetSuffix(), GetBaseName(), and GetCompleteBaseName() */ std::string GetCompleteSuffix() const; /** * Returns \c true if this %ModuleResource object points to a directory and thus * may have child resources. * * @return \c true if this object points to a directory, \c false otherwise. */ bool IsDir() const; /** * Returns \c true if this %ModuleResource object points to a file resource. * * @return \c true if this object points to an embedded file, \c false otherwise. */ bool IsFile() const; /** * Returns a list of resource names which are children of this object. * - * The returned names are relative to the path of this %ModuleResource object and - * may contain duplicates in case of modules which are statically linked into the - * module from which this object was retreived. + * The returned names are relative to the path of this %ModuleResource object + * and may contain file as well as directory entries. * * @return A list of child resource names. */ std::vector GetChildren() const; /** - * Returns the size of the raw embedded data for this %ModuleResource object. + * Returns a list of resource objects which are children of this object. + * + * The return ModuleResource objects may contain files as well as + * directory resources. + * + * @return A list of child resource objects. + */ + std::vector GetChildResources() const; + + /** + * Returns the size of the resource data for this %ModuleResource object. * * @return The resource data size. */ int GetSize() const; /** - * Returns a data pointer pointing to the raw data of this %ModuleResource object. - * If the resource is compressed the data returned is compressed and UncompressResourceData() - * must be used to access the data. If the resource represents a directory \c 0 is returned. + * Returns the last modified time of this resource in seconds from the epoch. * - * @return A raw pointer to the embedded data, or \c 0 if the resource is not a file resource. + * @return Last modified time of this resource. */ - const unsigned char* GetData() const; + time_t GetLastModified() const; private: - ModuleResource(const std::string& file, ModuleResourceTree* resourceTree, - const std::vector& resourceTrees); + ModuleResource(const std::string& file, const ModuleResourceContainer& resourceContainer); + ModuleResource(int index, const ModuleResourceContainer& resourceContainer); friend class Module; friend class ModulePrivate; + friend class ModuleResourceContainer; + friend class ModuleResourceStream; US_HASH_FUNCTION_FRIEND(ModuleResource); std::size_t Hash() const; + void* GetData() const; + ModuleResourcePrivate* d; }; US_END_NAMESPACE US_MSVC_POP_WARNING /** * \ingroup MicroServices */ US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ModuleResource)& resource); US_HASH_FUNCTION_NAMESPACE_BEGIN US_HASH_FUNCTION_BEGIN(US_PREPEND_NAMESPACE(ModuleResource)) return arg.Hash(); US_HASH_FUNCTION_END US_HASH_FUNCTION_NAMESPACE_END #endif // USMODULERESOURCE_H diff --git a/Core/CppMicroServices/core/include/usModuleResourceStream.h b/Core/CppMicroServices/core/include/usModuleResourceStream.h index 047439e606..0598e7bb44 100644 --- a/Core/CppMicroServices/core/include/usModuleResourceStream.h +++ b/Core/CppMicroServices/core/include/usModuleResourceStream.h @@ -1,65 +1,70 @@ -/*=================================================================== +/*============================================================================= -BlueBerry Platform + Library: CppMicroServices -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -See LICENSE.txt or http://www.mitk.org for details. + http://www.apache.org/licenses/LICENSE-2.0 -===================================================================*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ #ifndef USMODULERESOURCESTREAM_H #define USMODULERESOURCESTREAM_H #include "usModuleResourceBuffer_p.h" #include US_BEGIN_NAMESPACE class ModuleResource; /** * \ingroup MicroServices * * An input stream class for ModuleResource objects. * * This class provides access to the resource data embedded in a module's * shared library via a STL input stream interface. * * \see ModuleResource for an example how to use this class. */ class US_Core_EXPORT ModuleResourceStream : private ModuleResourceBuffer, public std::istream { public: /** * Construct a %ModuleResourceStream object. * * @param resource The ModuleResource object for which an input stream * should be constructed. * @param mode The open mode of the stream. If \c std::ios_base::binary * is used, the resource data will be treated as binary data, otherwise * the data is interpreted as text data and the usual platform specific * end-of-line translations take place. */ ModuleResourceStream(const ModuleResource& resource, std::ios_base::openmode mode = std::ios_base::in); private: // purposely not implemented ModuleResourceStream(const ModuleResourceStream&); ModuleResourceStream& operator=(const ModuleResourceStream&); }; US_END_NAMESPACE #endif // USMODULERESOURCESTREAM_H diff --git a/Core/CppMicroServices/core/include/usModuleSettings.h b/Core/CppMicroServices/core/include/usModuleSettings.h index 5593c723f6..9be332bac8 100644 --- a/Core/CppMicroServices/core/include/usModuleSettings.h +++ b/Core/CppMicroServices/core/include/usModuleSettings.h @@ -1,153 +1,171 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULESETTINGS_H #define USMODULESETTINGS_H #include "usCoreConfig.h" #include #include US_BEGIN_NAMESPACE /** * \ingroup MicroServices * * Query and set certain properties of the CppMicroServices library. * * The following environment variables influence the runtime behavior * of the CppMicroServices library: * * - \e US_DISABLE_AUTOLOADING If set, auto-loading of modules is disabled. * - \e US_AUTOLOAD_PATHS A ':' (Unix) or ';' (Windows) separated list of paths * from which modules should be auto-loaded. * * \remarks This class is thread safe. */ class US_Core_EXPORT ModuleSettings { public: typedef std::vector PathList; /** * Returns a special string which can be used as an argument for a * AddAutoLoadPath() call. * * When a module is loaded and this string has been added as a path * to the list of auto-load paths the CppMicroServices library will * auto-load all modules from the currently being loaded module's * auto-load directory. * * \return A string to be used in AddAutoLoadPath(). * * \remarks The returned string is contained in the default set of * auto-load paths, unless a new set of paths is given by a call to * SetAutoLoadPaths(). * * \sa MicroServices_AutoLoading * \sa US_INITIALIZE_MODULE */ static std::string CURRENT_MODULE_PATH(); /** * \return \c true if threading support has been configured into the * CppMicroServices library, \c false otherwise. */ static bool IsThreadingSupportEnabled(); /** * \return \c true if support for module auto-loading is enabled, * \c false otherwise. * * \remarks This method will always return \c false if support for auto-loading * has not been configured into the CppMicroServices library or if it has been * disabled by defining the US_DISABLE_AUTOLOADING environment variable. */ static bool IsAutoLoadingEnabled(); /** * Enable or disable auto-loading support. * * \param enable If \c true, enable auto-loading support, disable it otherwise. * * \remarks Calling this method will have no effect if support for * auto-loading has not been configured into the CppMicroServices library of it * it has been disabled by defining the US_DISABLE_AUTOLOADING envrionment variable. */ static void SetAutoLoadingEnabled(bool enable); /** * \return A list of paths in the file-system from which modules will be * auto-loaded. */ static PathList GetAutoLoadPaths(); /** * Set a list of paths in the file-system from which modules should be * auto-loaded. * @param paths A list of absolute file-system paths. */ static void SetAutoLoadPaths(const PathList& paths); /** * Add a path in the file-system to the list of paths from which modules * will be auto-loaded. * * @param path The additional absolute auto-load path in the file-system. */ static void AddAutoLoadPath(const std::string& path); /** * Set a local storage path for persistend module data. * * This path is used as a base directory for providing modules * with a storage path for writing persistent data. The callee * must ensure that the provided path exists and is writable. * * @see ModuleContext::GetDataFile(const std::string&) * * @param path An absolute path for writing persistent data. */ static void SetStoragePath(const std::string& path); /** * Get the absolute path for persistent data. The returned path * might be empty. If the path is non-empty, it is safe to assume * that the path exists and is writable. * * @return The absolute path to the persistent storage path. */ static std::string GetStoragePath(); + /** + * Set the logging level for log messages from CppMicroServices modules. + * + * Higher logging levels will discard messages with lower priority. + * E.g. a logging level of WarningMsg will discard all messages of + * type DebugMsg and InfoMsg. + * + * @param level The new logging level. + */ + static void SetLogLevel(MsgType level); + + /** + * Get the current logging level. + * + * @return The currently used logging level. + */ + static MsgType GetLogLevel(); + private: // purposely not implemented ModuleSettings(); ModuleSettings(const ModuleSettings&); ModuleSettings& operator=(const ModuleSettings&); }; US_END_NAMESPACE #endif // USMODULESETTINGS_H diff --git a/Core/CppMicroServices/core/include/usServiceEvent.h b/Core/CppMicroServices/core/include/usServiceEvent.h index 99e3aaec12..abcc2c3753 100644 --- a/Core/CppMicroServices/core/include/usServiceEvent.h +++ b/Core/CppMicroServices/core/include/usServiceEvent.h @@ -1,197 +1,197 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICEEVENT_H #define USSERVICEEVENT_H #ifdef REGISTERED #ifdef _WIN32 #error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\ enum type. Try to reorder your includes, compile with WIN32_LEAN_AND_MEAN, or undef\ the REGISTERED macro befor including this header. #else #error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\ enum type. Try to reorder your includes or undef the REGISTERED macro befor including\ this header. #endif #endif #include "usSharedData.h" #include "usServiceReference.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4251) #endif US_BEGIN_NAMESPACE class ServiceEventData; /** * \ingroup MicroServices * * An event from the Micro Services framework describing a service lifecycle change. *

* ServiceEvent objects are delivered to * listeners connected via ModuleContext::AddServiceListener() when a * change occurs in this service's lifecycle. A type code is used to identify * the event type for future extendability. */ class US_Core_EXPORT ServiceEvent { SharedDataPointer d; public: enum Type { /** * This service has been registered. *

* This event is delivered after the service * has been registered with the framework. * * @see ModuleContext#RegisterService() */ REGISTERED = 0x00000001, /** * The properties of a registered service have been modified. *

* This event is delivered after the service * properties have been modified. * * @see ServiceRegistration#SetProperties */ MODIFIED = 0x00000002, /** * This service is in the process of being unregistered. *

* This event is delivered before the service * has completed unregistering. * *

* If a module is using a service that is UNREGISTERING, the * module should release its use of the service when it receives this event. * If the module does not release its use of the service when it receives * this event, the framework will automatically release the module's use of * the service while completing the service unregistration operation. * * @see ServiceRegistration#Unregister * @see ModuleContext#UngetService */ UNREGISTERING = 0x00000004, /** * The properties of a registered service have been modified and the new * properties no longer match the listener's filter. *

* This event is delivered after the service * properties have been modified. This event is only delivered to listeners * which were added with a non-empty filter where the filter * matched the service properties prior to the modification but the filter * does not match the modified service properties. * * @see ServiceRegistration#SetProperties */ MODIFIED_ENDMATCH = 0x00000008 }; /** * Creates an invalid instance. */ ServiceEvent(); ~ServiceEvent(); /** * Can be used to check if this ServiceEvent instance is valid, * or if it has been constructed using the default constructor. * * @return true if this event object is valid, * false otherwise. */ bool IsNull() const; /** * Creates a new service event object. * * @param type The event type. * @param reference A ServiceReference object to the service * that had a lifecycle change. */ ServiceEvent(Type type, const ServiceReferenceBase& reference); ServiceEvent(const ServiceEvent& other); ServiceEvent& operator=(const ServiceEvent& other); /** * Returns a reference to the service that had a change occur in its * lifecycle. *

* This reference is the source of the event. * * @return Reference to the service that had a lifecycle change. */ ServiceReferenceU GetServiceReference() const; template ServiceReference GetServiceReference(InterfaceType) const { return GetServiceReference(); } /** * Returns the type of event. The event type values are: *

    *
  • {@link #REGISTERED}
  • *
  • {@link #MODIFIED}
  • *
  • {@link #MODIFIED_ENDMATCH}
  • *
  • {@link #UNREGISTERING}
  • *
* * @return Type of service lifecycle change. */ Type GetType() const; }; -US_END_NAMESPACE - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - /** * \ingroup MicroServices * @{ */ -US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ServiceEvent::Type)& type); -US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ServiceEvent)& event); +US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ServiceEvent::Type& type); +US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ServiceEvent& event); /** @}*/ +US_END_NAMESPACE + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #endif // USSERVICEEVENT_H diff --git a/Core/CppMicroServices/core/include/usServiceEventListenerHook.h b/Core/CppMicroServices/core/include/usServiceEventListenerHook.h index 127a7aaf19..128f1c4077 100644 --- a/Core/CppMicroServices/core/include/usServiceEventListenerHook.h +++ b/Core/CppMicroServices/core/include/usServiceEventListenerHook.h @@ -1,72 +1,70 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICEEVENTLISTENERHOOK_H #define USSERVICEEVENTLISTENERHOOK_H #include "usServiceInterface.h" #include "usServiceListenerHook.h" #include "usShrinkableVector.h" #include "usShrinkableMap.h" US_BEGIN_NAMESPACE class ModuleContext; class ServiceEvent; /** * @ingroup MicroServices * * Service Event Listener Hook Service. * *

* Modules registering this service will be called during service * (register, modify, and unregister service) operations. * * @remarks Implementations of this interface are required to be thread-safe. */ struct US_Core_EXPORT ServiceEventListenerHook { typedef ShrinkableMap > ShrinkableMapType; virtual ~ServiceEventListenerHook(); /** * Event listener hook method. This method is called prior to service event * delivery when a publishing module registers, modifies or unregisters a * service. This method can filter the listeners which receive the event. * * @param event The service event to be delivered. * @param listeners A map of Module Contexts to a list of Listener * Infos for the module's listeners to which the specified event will * be delivered. The implementation of this method may remove module * contexts from the map and listener infos from the list * values to prevent the event from being delivered to the associated * listeners. */ virtual void Event(const ServiceEvent& event, ShrinkableMapType& listeners) = 0; }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(us::ServiceEventListenerHook, "org.cppmicroservices.ServiceEventListenerHook/2.0.0") - #endif // USSERVICEEVENTLISTENERHOOK_H diff --git a/Core/CppMicroServices/core/include/usServiceFindHook.h b/Core/CppMicroServices/core/include/usServiceFindHook.h index a3940887f3..98378ed7b9 100644 --- a/Core/CppMicroServices/core/include/usServiceFindHook.h +++ b/Core/CppMicroServices/core/include/usServiceFindHook.h @@ -1,76 +1,74 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICEFINDHOOK_H #define USSERVICEFINDHOOK_H #include "usServiceInterface.h" #include "usShrinkableVector.h" #include US_BEGIN_NAMESPACE class Module; class ModuleContext; class ServiceReferenceBase; /** * @ingroup MicroServices * * Service Find Hook Service. * *

* Modules registering this service will be called during service find * (get service references) operations. * * @remarks Implementations of this interface are required to be thread-safe. */ struct US_Core_EXPORT ServiceFindHook { virtual ~ServiceFindHook(); /** * Find hook method. This method is called during the service find operation * (for example, ModuleContext::GetServiceReferences()). This method can * filter the result of the find operation. * * @param context The module context of the module performing the find * operation. * @param name The class name of the services to find or an empty string to * find all services. * @param filter The filter criteria of the services to find or an empty string * for no filter criteria. * @param references A list of Service References to be returned as a result of the * find operation. The implementation of this method may remove * service references from the list to prevent the references from being * returned to the module performing the find operation. */ virtual void Find(const ModuleContext* context, const std::string& name, const std::string& filter, ShrinkableVector& references) = 0; }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(us::ServiceFindHook, "org.cppmicroservices.ServiceFindHook/2.0.0") - #endif // USSERVICEFINDHOOK_H diff --git a/Core/CppMicroServices/core/include/usServiceInterface.h b/Core/CppMicroServices/core/include/usServiceInterface.h index b9638b9a28..351a70b97f 100644 --- a/Core/CppMicroServices/core/include/usServiceInterface.h +++ b/Core/CppMicroServices/core/include/usServiceInterface.h @@ -1,342 +1,343 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICEINTERFACE_H #define USSERVICEINTERFACE_H #include #include #include #include #include +US_BEGIN_NAMESPACE +std::string GetDemangledName(const std::type_info& typeInfo); +US_END_NAMESPACE + /** * \ingroup MicroServices * - * Returns a unique id for a given type. + * Returns a unique id for a given type. By default, the + * demangled name of \c T is returned. * - * This template method is specialized in the macro - * #US_DECLARE_SERVICE_INTERFACE to return a unique id - * for each service interface. + * This template method may be specialized directly or be + * using the macro #US_DECLARE_SERVICE_INTERFACE to return + * a custom id for each service interface. * * @tparam T The service interface type. * @return A unique id for the service interface type T. */ -template inline const char* us_service_interface_iid(); +template std::string us_service_interface_iid() +{ + return US_PREPEND_NAMESPACE(GetDemangledName)(typeid(T)); +} /// \cond -template<> inline const char* us_service_interface_iid() { return ""; } +template<> inline std::string us_service_interface_iid() { return std::string(); } /// \endcond -#if defined(QT_DEBUG) || defined(QT_NO_DEBUG) -#include - -#define US_DECLARE_SERVICE_INTERFACE(_service_interface_type, _service_interface_id) \ - template<> inline const char* qobject_interface_iid<_service_interface_type*>() \ - { return _service_interface_id; } \ - template<> inline const char* us_service_interface_iid<_service_interface_type>() \ - { return _service_interface_id; } \ - template<> inline _service_interface_type* qobject_cast<_service_interface_type*>(QObject* object) \ - { return reinterpret_cast<_service_interface_type*>(object ? object->qt_metacast(_service_interface_id) : 0); } \ - template<> inline _service_interface_type* qobject_cast<_service_interface_type*>(const QObject* object) \ - { return reinterpret_cast<_service_interface_type*>(object ? const_cast(object)->qt_metacast(_service_interface_id) : 0); } - -#else /** * \ingroup MicroServices * - * \brief Declare a CppMicroServices service interface. + * \brief Declare a service interface id. * * This macro associates the given identifier \e _service_interface_id (a string literal) to the * interface class called _service_interface_type. The Identifier must be unique. For example: * * \code * #include * * struct ISomeInterace { ... }; * * US_DECLARE_SERVICE_INTERFACE(ISomeInterface, "com.mycompany.service.ISomeInterface/1.0") * \endcode * - * This macro is normally used right after the class definition for _service_interface_type, in a header file. + * The usage of this macro is optional and the service interface id which is automatically + * associated with any type is usually good enough (the demangled type name). However, care must + * be taken if the default id is compared with a string literal hard-coding a service interface + * id. E.g. the default id for templated types in the STL may differ between platforms. For + * user-defined types and templates the ids are typically consistent, but platform specific + * default template arguments will lead to different ids. + * + * This macro is normally used right after the class definition for _service_interface_type, + * in a header file. * * If you want to use #US_DECLARE_SERVICE_INTERFACE with interface classes declared in a * namespace then you have to make sure the #US_DECLARE_SERVICE_INTERFACE macro call is not * inside a namespace though. For example: * * \code * #include * * namespace Foo * { * struct ISomeInterface { ... }; * } * * US_DECLARE_SERVICE_INTERFACE(Foo::ISomeInterface, "com.mycompany.service.ISomeInterface/1.0") * \endcode * * @param _service_interface_type The service interface type. * @param _service_interface_id A string literal representing a globally unique identifier. */ #define US_DECLARE_SERVICE_INTERFACE(_service_interface_type, _service_interface_id) \ - template<> inline const char* us_service_interface_iid<_service_interface_type>() \ + template<> inline std::string us_service_interface_iid<_service_interface_type>() \ { return _service_interface_id; } \ -#endif US_BEGIN_NAMESPACE class ServiceFactory; /** * @ingroup MicroServices * * A helper type used in several methods to get proper * method overload resolutions. */ template struct InterfaceType {}; /** * @ingroup MicroServices * * A map containing interfaces ids and their corresponding service object * pointers. InterfaceMap instances represent a complete service object * which implementes one or more service interfaces. For each implemented * service interface, there is an entry in the map with the key being * the service interface id and the value a pointer to the service * interface implementation. * * To create InterfaceMap instances, use the MakeInterfaceMap helper class. * * @note This is a low-level type and should only rarely be used. * * @see MakeInterfaceMap */ typedef std::map InterfaceMap; template bool InsertInterfaceType(InterfaceMap& im, I* i) { - if (us_service_interface_iid() == NULL) + if (us_service_interface_iid().empty()) { throw ServiceException(std::string("The interface class ") + typeid(I).name() + " uses an invalid id in its US_DECLARE_SERVICE_INTERFACE macro call."); } im.insert(std::make_pair(std::string(us_service_interface_iid()), static_cast(static_cast(i)))); return true; } template<> inline bool InsertInterfaceType(InterfaceMap&, void*) { return false; } /** * @ingroup MicroServices * * Helper class for constructing InterfaceMap instances based * on service implementations or service factories. * * Example usage: * \code * MyService service; // implementes I1 and I2 * InterfaceMap im = MakeInterfaceMap(&service); * \endcode * * The MakeInterfaceMap supports service implementations with * up to three service interfaces. * * @see InterfaceMap */ template struct MakeInterfaceMap { ServiceFactory* m_factory; I1* m_interface1; I2* m_interface2; I3* m_interface3; /** * Constructor taking a service implementation pointer. * * @param impl A service implementation pointer, which must * be castable to a all specified service interfaces. */ template MakeInterfaceMap(Impl* impl) : m_factory(NULL) , m_interface1(static_cast(impl)) , m_interface2(static_cast(impl)) , m_interface3(static_cast(impl)) {} /** * Constructor taking a service factory. * * @param factory A service factory. */ MakeInterfaceMap(ServiceFactory* factory) : m_factory(factory) , m_interface1(NULL) , m_interface2(NULL) , m_interface3(NULL) { if (factory == NULL) { throw ServiceException("The service factory argument must not be NULL."); } } operator InterfaceMap () { InterfaceMap sim; InsertInterfaceType(sim, m_interface1); InsertInterfaceType(sim, m_interface2); InsertInterfaceType(sim, m_interface3); if (m_factory) { sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"), static_cast(m_factory))); } return sim; } }; /// \cond template struct MakeInterfaceMap { ServiceFactory* m_factory; I1* m_interface1; I2* m_interface2; template MakeInterfaceMap(Impl* impl) : m_factory(NULL) , m_interface1(static_cast(impl)) , m_interface2(static_cast(impl)) {} MakeInterfaceMap(ServiceFactory* factory) : m_factory(factory) , m_interface1(NULL) , m_interface2(NULL) { if (factory == NULL) { throw ServiceException("The service factory argument must not be NULL."); } } operator InterfaceMap () { InterfaceMap sim; InsertInterfaceType(sim, m_interface1); InsertInterfaceType(sim, m_interface2); if (m_factory) { sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"), static_cast(m_factory))); } return sim; } }; template struct MakeInterfaceMap { ServiceFactory* m_factory; I1* m_interface1; template MakeInterfaceMap(Impl* impl) : m_factory(NULL) , m_interface1(static_cast(impl)) {} MakeInterfaceMap(ServiceFactory* factory) : m_factory(factory) , m_interface1(NULL) { if (factory == NULL) { throw ServiceException("The service factory argument must not be NULL."); } } operator InterfaceMap () { InterfaceMap sim; InsertInterfaceType(sim, m_interface1); if (m_factory) { sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"), static_cast(m_factory))); } return sim; } }; template<> struct MakeInterfaceMap; /// \endcond /** * @ingroup MicroServices * * Extract a service interface pointer from a given InterfaceMap instance. * * @param map a InterfaceMap instance. * @return The service interface pointer for the service interface id of the * \c I1 interface type or NULL if \c map does not contain an entry * for the given type. * * @see MakeInterfaceMap */ template I1* ExtractInterface(const InterfaceMap& map) { InterfaceMap::const_iterator iter = map.find(us_service_interface_iid()); if (iter != map.end()) { return reinterpret_cast(iter->second); } return NULL; } US_END_NAMESPACE #endif // USSERVICEINTERFACE_H diff --git a/Core/CppMicroServices/core/include/usServiceListenerHook.h b/Core/CppMicroServices/core/include/usServiceListenerHook.h index 6fd3598e12..0c7c077621 100644 --- a/Core/CppMicroServices/core/include/usServiceListenerHook.h +++ b/Core/CppMicroServices/core/include/usServiceListenerHook.h @@ -1,176 +1,174 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICELISTENERHOOK_H #define USSERVICELISTENERHOOK_H #include "usServiceInterface.h" #include "usShrinkableVector.h" #include "usSharedData.h" #include US_BEGIN_NAMESPACE class ModuleContext; class ServiceListenerEntry; /** * @ingroup MicroServices * * Service Listener Hook Service. * *

* Modules registering this service will be called during service listener * addition and removal. * * @remarks Implementations of this interface are required to be thread-safe. */ struct US_Core_EXPORT ServiceListenerHook { class ListenerInfoData; /** * Information about a Service Listener. This class describes the module * which added the Service Listener and the filter with which it was added. * * @remark This class is not intended to be implemented by clients. */ struct US_Core_EXPORT ListenerInfo { ListenerInfo(); ListenerInfo(const ListenerInfo& other); ~ListenerInfo(); ListenerInfo& operator=(const ListenerInfo& other); /** * Can be used to check if this ListenerInfo instance is valid, * or if it has been constructed using the default constructor. * * @return true if this listener object is valid, * false otherwise. */ bool IsNull() const; /** * Return the context of the module which added the listener. * * @return The context of the module which added the listener. */ ModuleContext* GetModuleContext() const; /** * Return the filter string with which the listener was added. * * @return The filter string with which the listener was added. This may * be empty if the listener was added without a filter. */ std::string GetFilter() const; /** * Return the state of the listener for this addition and removal life * cycle. Initially this method will return \c false indicating the * listener has been added but has not been removed. After the listener * has been removed, this method must always returns \c true. * *

* There is an extremely rare case in which removed notification to * {@link ServiceListenerHook}s can be made before added notification if two * threads are racing to add and remove the same service listener. * Because {@link ServiceListenerHook}s are called synchronously during service * listener addition and removal, the CppMicroServices library cannot guarantee * in-order delivery of added and removed notification for a given * service listener. This method can be used to detect this rare * occurrence. * * @return \c false if the listener has not been been removed, * \c true otherwise. */ bool IsRemoved() const; /** * Compares this \c ListenerInfo to another \c ListenerInfo. * Two {@code ListenerInfo}s are equal if they refer to the same * listener for a given addition and removal life cycle. If the same * listener is added again, it will have a different * \c ListenerInfo which is not equal to this \c ListenerInfo. * * @param other The object to compare against this \c ListenerInfo. * @return \c true if the other object is a \c ListenerInfo * object and both objects refer to the same listener for a * given addition and removal life cycle. */ bool operator==(const ListenerInfo& other) const; private: friend class ServiceListenerEntry; US_HASH_FUNCTION_FRIEND(ServiceListenerHook::ListenerInfo); ListenerInfo(ListenerInfoData* data); ExplicitlySharedDataPointer d; }; virtual ~ServiceListenerHook(); /** * Added listeners hook method. This method is called to provide the hook * implementation with information on newly added service listeners. This * method will be called as service listeners are added while this hook is * registered. Also, immediately after registration of this hook, this * method will be called to provide the current collection of service * listeners which had been added prior to the hook being registered. * * @param listeners A collection of \c ListenerInfo objects for newly added * service listeners which are now listening to service events. */ virtual void Added(const std::vector& listeners) = 0; /** * Removed listeners hook method. This method is called to provide the hook * implementation with information on newly removed service listeners. This * method will be called as service listeners are removed while this hook is * registered. * * @param listeners A collection of \c ListenerInfo objects for newly removed * service listeners which are no longer listening to service events. */ virtual void Removed(const std::vector& listeners) = 0; }; US_END_NAMESPACE US_HASH_FUNCTION_NAMESPACE_BEGIN US_HASH_FUNCTION_BEGIN(US_PREPEND_NAMESPACE(ServiceListenerHook::ListenerInfo)) return US_HASH_FUNCTION(const US_PREPEND_NAMESPACE(ServiceListenerHook::ListenerInfoData)*, arg.d.Data()); US_HASH_FUNCTION_END US_HASH_FUNCTION_NAMESPACE_END -US_DECLARE_SERVICE_INTERFACE(us::ServiceListenerHook, "org.cppmicroservices.ServiceListenerHook/2.0.0") - #endif // USSERVICELISTENERHOOK_H diff --git a/Core/CppMicroServices/core/include/usServiceTracker.h b/Core/CppMicroServices/core/include/usServiceTracker.h index ab0f7e77f5..5d2f794fd6 100644 --- a/Core/CppMicroServices/core/include/usServiceTracker.h +++ b/Core/CppMicroServices/core/include/usServiceTracker.h @@ -1,600 +1,597 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICETRACKER_H #define USSERVICETRACKER_H #include #include "usServiceReference.h" #include "usServiceTrackerCustomizer.h" #include "usLDAPFilter.h" US_BEGIN_NAMESPACE template class TrackedService; template class ServiceTrackerPrivate; class ModuleContext; /** * \ingroup MicroServices * * A base class template for type traits for objects tracked by a * ServiceTracker instance. It provides the \c TrackedType typedef * and two dummy method definitions. * * Tracked type traits (TTT) classes must additionally provide the * following methods: * *

    *
  • static bool IsValid(const TrackedType& t) Returns \c true if \c t is a valid object, \c false otherwise.
  • *
  • static void Dispose(TrackedType& t) Clears any resources held by the tracked object \c t.
  • *
  • static TrackedType DefaultValue() Returns the default value for newly created tracked objects.
  • *
* * @tparam T The type of the tracked object. * @tparam TTT The tracked type traits class deriving from this class. * * @see ServiceTracker */ template struct TrackedTypeTraitsBase { typedef T TrackedType; // Needed for S == void static TrackedType ConvertToTrackedType(const InterfaceMap&) { throw std::runtime_error("A custom ServiceTrackerCustomizer instance is required for custom tracked objects."); - return TTT::DefaultValue(); + //return TTT::DefaultValue(); } // Needed for S != void static TrackedType ConvertToTrackedType(void*) { throw std::runtime_error("A custom ServiceTrackerCustomizer instance is required for custom tracked objects."); - return TTT::DefaultValue(); + //return TTT::DefaultValue(); } }; /// \cond template struct TrackedTypeTraits; /// \endcond /** * \ingroup MicroServices * * Default type traits for custom tracked objects of pointer type. * * Use this tracked type traits template for custom tracked objects of * pointer type with the ServiceTracker class. * * @tparam S The type of the service being tracked. * @tparam T The type of the tracked object. */ template struct TrackedTypeTraits : public TrackedTypeTraitsBase > { typedef T* TrackedType; static bool IsValid(const TrackedType& t) { return t != NULL; } static TrackedType DefaultValue() { return NULL; } static void Dispose(TrackedType& t) { t = 0; } }; /// \cond template struct TrackedTypeTraits { typedef S* TrackedType; static bool IsValid(const TrackedType& t) { return t != NULL; } static TrackedType DefaultValue() { return NULL; } static void Dispose(TrackedType& t) { t = 0; } static TrackedType ConvertToTrackedType(S* s) { return s; } }; /// \endcond /// \cond /* * This specialization is "special" because the tracked type is not * void* (as specified in the second template parameter) but InterfaceMap. * This is in line with the ModuleContext::GetService(...) overloads to * return either S* or InterfaceMap dependening on the template parameter. */ template<> struct TrackedTypeTraits { typedef InterfaceMap TrackedType; static bool IsValid(const TrackedType& t) { return !t.empty(); } static TrackedType DefaultValue() { return TrackedType(); } static void Dispose(TrackedType& t) { t.clear(); } static TrackedType ConvertToTrackedType(const InterfaceMap& im) { return im; } }; /// \endcond /** * \ingroup MicroServices * * The ServiceTracker class simplifies using services from the * framework's service registry. *

* A ServiceTracker object is constructed with search criteria and * a ServiceTrackerCustomizer object. A ServiceTracker * can use a ServiceTrackerCustomizer to customize the service * objects to be tracked. The ServiceTracker can then be opened to * begin tracking all services in the framework's service registry that match * the specified search criteria. The ServiceTracker correctly * handles all of the details of listening to ServiceEvents and * getting and ungetting services. *

* The GetServiceReferences method can be called to get references * to the services being tracked. The GetService and * GetServices methods can be called to get the service objects for * the tracked service. * * \note The ServiceTracker class is thread-safe. It does not call a * ServiceTrackerCustomizer while holding any locks. * ServiceTrackerCustomizer implementations must also be * thread-safe. * * Customization of the services to be tracked requires a custom tracked type traits * class if the custom tracked type is not a pointer type. To customize a tracked * service using a custom type with value-semantics like * \snippet uServices-servicetracker/main.cpp tt * the custom tracked type traits class should look like this: * \snippet uServices-servicetracker/main.cpp ttt * * For a custom tracked type, a ServiceTrackerCustomizer is required, which knows * how to associate the tracked service with the custom tracked type: * \snippet uServices-servicetracker/main.cpp customizer * The custom tracking type traits class and customizer can now be used to instantiate * a ServiceTracker: * \snippet uServices-servicetracker/main.cpp tracker * * If the custom tracked type is a pointer type, a suitable tracked type traits * template is provided by the framework and only a ServiceTrackerCustomizer needs * to be provided: * \snippet uServices-servicetracker/main.cpp tracker2 * * * @tparam S The type of the service being tracked. The type S* must be an - * assignable datatype. Further, if the - * ServiceTracker(ModuleContext*, ServiceTrackerCustomizer*) - * constructor is used, the type must have an associated interface id via - * #US_DECLARE_SERVICE_INTERFACE. + * assignable datatype. * @tparam TTT Type traits of the tracked object. The type traits class provides * information about the customized service object, see TrackedTypeTraitsBase. * * @remarks This class is thread safe. */ template > class ServiceTracker : protected ServiceTrackerCustomizer { public: /// The type of the service being tracked typedef S ServiceType; /// The type of the tracked object typedef typename TTT::TrackedType T; typedef ServiceReference ServiceReferenceType; typedef std::map,T> TrackingMap; ~ServiceTracker(); /** * Create a ServiceTracker on the specified * ServiceReference. * *

* The service referenced by the specified ServiceReference * will be tracked by this ServiceTracker. * * @param context The ModuleContext against which the tracking * is done. * @param reference The ServiceReference for the service to be * tracked. * @param customizer The customizer object to call when services are added, * modified, or removed in this ServiceTracker. If * customizer is null, then this * ServiceTracker will be used as the * ServiceTrackerCustomizer and this * ServiceTracker will call the * ServiceTrackerCustomizer methods on itself. */ ServiceTracker(ModuleContext* context, const ServiceReferenceType& reference, ServiceTrackerCustomizer* customizer = 0); /** * Create a ServiceTracker on the specified class name. * *

* Services registered under the specified class name will be tracked by * this ServiceTracker. * * @param context The ModuleContext against which the tracking * is done. * @param clazz The class name of the services to be tracked. * @param customizer The customizer object to call when services are added, * modified, or removed in this ServiceTracker. If * customizer is null, then this * ServiceTracker will be used as the * ServiceTrackerCustomizer and this * ServiceTracker will call the * ServiceTrackerCustomizer methods on itself. */ ServiceTracker(ModuleContext* context, const std::string& clazz, ServiceTrackerCustomizer* customizer = 0); /** * Create a ServiceTracker on the specified * LDAPFilter object. * *

* Services which match the specified LDAPFilter object will be * tracked by this ServiceTracker. * * @param context The ModuleContext against which the tracking * is done. * @param filter The LDAPFilter to select the services to be * tracked. * @param customizer The customizer object to call when services are added, * modified, or removed in this ServiceTracker. If * customizer is null, then this ServiceTracker will be * used as the ServiceTrackerCustomizer and this * ServiceTracker will call the * ServiceTrackerCustomizer methods on itself. */ ServiceTracker(ModuleContext* context, const LDAPFilter& filter, ServiceTrackerCustomizer* customizer = 0); /** * Create a ServiceTracker on the class template * argument S. * *

* Services registered under the interface name of the class template * argument S will be tracked by this ServiceTracker. * * @param context The ModuleContext against which the tracking * is done. * @param customizer The customizer object to call when services are added, * modified, or removed in this ServiceTracker. If * customizer is null, then this ServiceTracker will be * used as the ServiceTrackerCustomizer and this * ServiceTracker will call the * ServiceTrackerCustomizer methods on itself. */ ServiceTracker(ModuleContext* context, ServiceTrackerCustomizer* customizer = 0); /** * Open this ServiceTracker and begin tracking services. * *

* Services which match the search criteria specified when this * ServiceTracker was created are now tracked by this * ServiceTracker. * * @throws std::logic_error If the ModuleContext * with which this ServiceTracker was created is no * longer valid. */ virtual void Open(); /** * Close this ServiceTracker. * *

* This method should be called when this ServiceTracker should * end the tracking of services. * *

* This implementation calls GetServiceReferences() to get the list * of tracked services to remove. */ virtual void Close(); /** * Wait for at least one service to be tracked by this * ServiceTracker. This method will also return when this * ServiceTracker is closed. * *

* It is strongly recommended that WaitForService is not used * during the calling of the ModuleActivator methods. * ModuleActivator methods are expected to complete in a short * period of time. * *

* This implementation calls GetService() to determine if a service * is being tracked. * * @return Returns the result of GetService(). */ virtual T WaitForService(unsigned long timeoutMillis = 0); /** * Return a list of ServiceReferences for all services being * tracked by this ServiceTracker. * * @return List of ServiceReferences. */ virtual std::vector GetServiceReferences() const; /** * Returns a ServiceReference for one of the services being * tracked by this ServiceTracker. * *

* If multiple services are being tracked, the service with the highest * ranking (as specified in its service.ranking property) is * returned. If there is a tie in ranking, the service with the lowest * service ID (as specified in its service.id property); that * is, the service that was registered first is returned. This is the same * algorithm used by ModuleContext::GetServiceReference(). * *

* This implementation calls GetServiceReferences() to get the list * of references for the tracked services. * * @return A ServiceReference for a tracked service. * @throws ServiceException if no services are being tracked. */ virtual ServiceReferenceType GetServiceReference() const; /** * Returns the service object for the specified * ServiceReference if the specified referenced service is * being tracked by this ServiceTracker. * * @param reference The reference to the desired service. * @return A service object or null if the service referenced * by the specified ServiceReference is not being * tracked. */ virtual T GetService(const ServiceReferenceType& reference) const; /** * Return a list of service objects for all services being tracked by this * ServiceTracker. * *

* This implementation calls GetServiceReferences() to get the list * of references for the tracked services and then calls * GetService(const ServiceReference&) for each reference to get the * tracked service object. * * @return A list of service objects or an empty list if no services * are being tracked. */ virtual std::vector GetServices() const; /** * Returns a service object for one of the services being tracked by this * ServiceTracker. * *

* If any services are being tracked, this implementation returns the result * of calling %GetService(%GetServiceReference()). * * @return A service object or null if no services are being * tracked. */ virtual T GetService() const; /** * Remove a service from this ServiceTracker. * * The specified service will be removed from this * ServiceTracker. If the specified service was being tracked * then the ServiceTrackerCustomizer::RemovedService method will * be called for that service. * * @param reference The reference to the service to be removed. */ virtual void Remove(const ServiceReferenceType& reference); /** * Return the number of services being tracked by this * ServiceTracker. * * @return The number of services being tracked. */ virtual int Size() const; /** * Returns the tracking count for this ServiceTracker. * * The tracking count is initialized to 0 when this * ServiceTracker is opened. Every time a service is added, * modified or removed from this ServiceTracker, the tracking * count is incremented. * *

* The tracking count can be used to determine if this * ServiceTracker has added, modified or removed a service by * comparing a tracking count value previously collected with the current * tracking count value. If the value has not changed, then no service has * been added, modified or removed from this ServiceTracker * since the previous tracking count was collected. * * @return The tracking count for this ServiceTracker or -1 if * this ServiceTracker is not open. */ virtual int GetTrackingCount() const; /** * Return a sorted map of the ServiceReferences and * service objects for all services being tracked by this * ServiceTracker. The map is sorted in natural order * of ServiceReference. That is, the last entry is the service * with the highest ranking and the lowest service id. * * @param tracked A TrackingMap with the ServiceReferences * and service objects for all services being tracked by this * ServiceTracker. If no services are being tracked, * then the returned map is empty. */ virtual void GetTracked(TrackingMap& tracked) const; /** * Return if this ServiceTracker is empty. * * @return true if this ServiceTracker is not tracking any * services. */ virtual bool IsEmpty() const; protected: /** * Default implementation of the * ServiceTrackerCustomizer::AddingService method. * *

* This method is only called when this ServiceTracker has been * constructed with a null ServiceTrackerCustomizer argument. * *

* This implementation returns the result of calling GetService * on the ModuleContext with which this * ServiceTracker was created passing the specified * ServiceReference. *

* This method can be overridden in a subclass to customize the service * object to be tracked for the service being added. In that case, take care * not to rely on the default implementation of * \link RemovedService(const ServiceReferenceType&, T service) removedService\endlink * to unget the service. * * @param reference The reference to the service being added to this * ServiceTracker. * @return The service object to be tracked for the service added to this * ServiceTracker. * @see ServiceTrackerCustomizer::AddingService(const ServiceReference&) */ T AddingService(const ServiceReferenceType& reference); /** * Default implementation of the * ServiceTrackerCustomizer::ModifiedService method. * *

* This method is only called when this ServiceTracker has been * constructed with a null ServiceTrackerCustomizer argument. * *

* This implementation does nothing. * * @param reference The reference to modified service. * @param service The service object for the modified service. * @see ServiceTrackerCustomizer::ModifiedService(const ServiceReference&, T) */ void ModifiedService(const ServiceReferenceType& reference, T service); /** * Default implementation of the * ServiceTrackerCustomizer::RemovedService method. * *

* This method is only called when this ServiceTracker has been * constructed with a null ServiceTrackerCustomizer argument. * *

* This implementation calls UngetService, on the * ModuleContext with which this ServiceTracker * was created, passing the specified ServiceReference. *

* This method can be overridden in a subclass. If the default * implementation of \link AddingService(const ServiceReferenceType&) AddingService\endlink * method was used, this method must unget the service. * * @param reference The reference to removed service. * @param service The service object for the removed service. * @see ServiceTrackerCustomizer::RemovedService(const ServiceReferenceType&, T) */ void RemovedService(const ServiceReferenceType& reference, T service); private: typedef ServiceTracker _ServiceTracker; typedef TrackedService _TrackedService; typedef ServiceTrackerPrivate _ServiceTrackerPrivate; typedef ServiceTrackerCustomizer _ServiceTrackerCustomizer; friend class TrackedService; friend class ServiceTrackerPrivate; _ServiceTrackerPrivate* const d; }; US_END_NAMESPACE #include "usServiceTracker.tpp" #endif // USSERVICETRACKER_H diff --git a/Core/CppMicroServices/core/include/usUncompressResourceData.h b/Core/CppMicroServices/core/include/usUncompressResourceData.h deleted file mode 100644 index 131bf506a1..0000000000 --- a/Core/CppMicroServices/core/include/usUncompressResourceData.h +++ /dev/null @@ -1,33 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#ifndef USUNCOMPRESSRESOURCEDATA_H -#define USUNCOMPRESSRESOURCEDATA_H - -#include "usCoreConfig.h" - -US_BEGIN_NAMESPACE - -US_Core_EXPORT unsigned char* UncompressResourceData(const unsigned char* data, std::size_t size, std::size_t* uncompressedSize); - -US_END_NAMESPACE - -#endif // USUNCOMPRESSRESOURCEDATA_H diff --git a/Core/CppMicroServices/core/src/CMakeLists.txt b/Core/CppMicroServices/core/src/CMakeLists.txt index b86061fd43..aeb9cf14bf 100644 --- a/Core/CppMicroServices/core/src/CMakeLists.txt +++ b/Core/CppMicroServices/core/src/CMakeLists.txt @@ -1,86 +1,84 @@ #----------------------------------------------------------------------------- # Source files #----------------------------------------------------------------------------- set(_srcs util/usAny.cpp - util/jsoncpp.cpp util/usLDAPProp.cpp util/usSharedLibrary.cpp - util/usUncompressResourceData.c - util/usUncompressResourceData.cpp util/usUtils.cpp service/usLDAPExpr.cpp service/usLDAPFilter.cpp service/usServiceException.cpp service/usServiceEvent.cpp service/usServiceEventListenerHook.cpp service/usServiceFindHook.cpp service/usServiceHooks.cpp service/usServiceListenerEntry.cpp service/usServiceListenerEntry_p.h service/usServiceListenerHook.cpp service/usServiceListeners.cpp service/usServiceListeners_p.h service/usServiceObjects.cpp service/usServiceProperties.cpp service/usServicePropertiesImpl.cpp service/usServiceReferenceBase.cpp service/usServiceReferenceBasePrivate.cpp service/usServiceRegistrationBase.cpp service/usServiceRegistrationBasePrivate.cpp service/usServiceRegistry.cpp service/usServiceRegistry_p.h module/usCoreModuleActivator.cpp module/usCoreModuleContext_p.h module/usCoreModuleContext.cpp module/usModuleContext.cpp module/usModule.cpp module/usModuleEvent.cpp module/usModuleEventHook.cpp module/usModuleFindHook.cpp module/usModuleHooks.cpp module/usModuleInfo.cpp module/usModuleManifest.cpp module/usModulePrivate.cpp module/usModuleRegistry.cpp module/usModuleResource.cpp module/usModuleResourceBuffer.cpp + module/usModuleResourceContainer.cpp module/usModuleResourceStream.cpp - module/usModuleResourceTree.cpp module/usModuleSettings.cpp module/usModuleUtils.cpp module/usModuleVersion.cpp + + ../../third_party/jsoncpp.cpp + ../../third_party/miniz.c ) set(_private_headers util/usAtomicInt_p.h util/usListenerFunctors_p.h util/usLog_p.h util/usStaticInit_p.h util/usThreads_p.h util/usUtils_p.h util/usWaitCondition_p.h - util/dirent_win32_p.h - service/usServiceHooks_p.h service/usServiceListenerHook_p.h service/usServicePropertiesImpl_p.h service/usServiceTracker.tpp service/usServiceTrackerPrivate.h service/usServiceTrackerPrivate.tpp service/usTrackedService_p.h service/usTrackedServiceListener_p.h service/usTrackedService.tpp module/usModuleAbstractTracked_p.h module/usModuleAbstractTracked.tpp module/usModuleHooks_p.h module/usModuleResourceBuffer_p.h - module/usModuleResourceTree_p.h + module/usModuleResourceContainer_p.h module/usModuleUtils_p.h ) diff --git a/Core/CppMicroServices/core/src/module/usCoreModuleActivator.cpp b/Core/CppMicroServices/core/src/module/usCoreModuleActivator.cpp index 4dd03e8eba..cf49fd7966 100644 --- a/Core/CppMicroServices/core/src/module/usCoreModuleActivator.cpp +++ b/Core/CppMicroServices/core/src/module/usCoreModuleActivator.cpp @@ -1,47 +1,47 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleActivator.h" #include "usModule.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" US_BEGIN_NAMESPACE class CoreModuleActivator : public ModuleActivator { void Load(ModuleContext* mc) { mc->GetModule()->d->coreCtx->Init(); } void Unload(ModuleContext* /*mc*/) { //mc->GetModule()->d->coreCtx->Uninit(); } }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(CppMicroServices, US_PREPEND_NAMESPACE(CoreModuleActivator)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(CoreModuleActivator)) diff --git a/Core/CppMicroServices/core/src/module/usModule.cpp b/Core/CppMicroServices/core/src/module/usModule.cpp index 77497b296b..976bd89666 100644 --- a/Core/CppMicroServices/core/src/module/usModule.cpp +++ b/Core/CppMicroServices/core/src/module/usModule.cpp @@ -1,316 +1,313 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModule.h" #include "usModuleContext.h" #include "usModuleActivator.h" #include "usModulePrivate.h" #include "usModuleResource.h" #include "usModuleSettings.h" #include "usCoreModuleContext_p.h" #include "usCoreConfig.h" US_BEGIN_NAMESPACE const std::string& Module::PROP_ID() { static const std::string s("module.id"); return s; } const std::string& Module::PROP_NAME() { static const std::string s("module.name"); return s; } const std::string& Module::PROP_LOCATION() { static const std::string s("module.location"); return s; } const std::string& Module::PROP_VERSION() { static const std::string s("module.version"); return s; } const std::string&Module::PROP_VENDOR() { static const std::string s("module.vendor"); return s; } const std::string&Module::PROP_DESCRIPTION() { static const std::string s("module.description"); return s; } const std::string&Module::PROP_AUTOLOAD_DIR() { static const std::string s("module.autoload_dir"); return s; } +const std::string&Module::PROP_AUTOLOADED_MODULES() +{ + static const std::string s("module.autoloaded_modules"); + return s; +} + Module::Module() : d(0) { } Module::~Module() { delete d; } void Module::Init(CoreModuleContext* coreCtx, ModuleInfo* info) { ModulePrivate* mp = new ModulePrivate(this, coreCtx, info); std::swap(mp, d); delete mp; } void Module::Uninit() { if (d->moduleContext != NULL) { //d->coreCtx->listeners.HooksModuleStopped(d->moduleContext); d->RemoveModuleResources(); delete d->moduleContext; d->moduleContext = 0; d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::UNLOADED, this)); d->moduleActivator = 0; } } bool Module::IsLoaded() const { return d->moduleContext != 0; } void Module::Start() { if (d->moduleContext) { US_WARN << "Module " << d->info.name << " already started."; return; } d->moduleContext = new ModuleContext(this->d); -// try -// { - d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::LOADING, this)); - // try to get a ModuleActivator instance - if (d->info.activatorHook) + typedef ModuleActivator*(*ModuleActivatorHook)(void); + ModuleActivatorHook activatorHook = NULL; + + std::string activator_func = "_us_module_activator_instance_" + d->info.name; + void* activatorHookSym = ModuleUtils::GetSymbol(d->info, activator_func.c_str()); + std::memcpy(&activatorHook, &activatorHookSym, sizeof(void*)); + + d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::LOADING, this)); + // try to get a ModuleActivator instance + + if (activatorHook) + { + try + { + d->moduleActivator = activatorHook(); + } + catch (...) { - try - { - d->moduleActivator = d->info.activatorHook(); - } - catch (...) - { - US_ERROR << "Creating the module activator of " << d->info.name << " failed"; - throw; - } - - d->moduleActivator->Load(d->moduleContext); + US_ERROR << "Creating the module activator of " << d->info.name << " failed"; + throw; } - d->StartStaticModules(); + // This method should be "noexcept" and by not catching exceptions + // here we semantically treat it that way since any exception during + // static initialization will either terminate the program or cause + // the dynamic loader to report an error. + d->moduleActivator->Load(d->moduleContext); + } #ifdef US_ENABLE_AUTOLOADING_SUPPORT - if (ModuleSettings::IsAutoLoadingEnabled()) + if (ModuleSettings::IsAutoLoadingEnabled()) + { + const std::vector loadedPaths = AutoLoadModules(d->info); + if (!loadedPaths.empty()) { - AutoLoadModules(d->info); + d->moduleManifest.SetValue(PROP_AUTOLOADED_MODULES(), Any(loadedPaths)); } + } #endif - d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::LOADED, this)); -// } -// catch (...) -// { -// d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::UNLOADING, this)); -// d->RemoveModuleResources(); - -// delete d->moduleContext; -// d->moduleContext = 0; - -// d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::UNLOADED, this)); -// US_ERROR << "Calling the module activator Load() method of " << d->info.name << " failed!"; -// throw; -// } + d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::LOADED, this)); } void Module::Stop() { if (d->moduleContext == 0) { US_WARN << "Module " << d->info.name << " already stopped."; return; } try { d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::UNLOADING, this)); - d->StopStaticModules(); - if (d->moduleActivator) { d->moduleActivator->Unload(d->moduleContext); } } catch (...) { US_WARN << "Calling the module activator Unload() method of " << d->info.name << " failed!"; try { this->Uninit(); } catch (...) {} throw; } this->Uninit(); } ModuleContext* Module::GetModuleContext() const { return d->moduleContext; } long Module::GetModuleId() const { return d->info.id; } std::string Module::GetLocation() const { return d->info.location; } std::string Module::GetName() const { return d->info.name; } ModuleVersion Module::GetVersion() const { return d->version; } Any Module::GetProperty(const std::string& key) const { return d->moduleManifest.GetValue(key); } std::vector Module::GetPropertyKeys() const { return d->moduleManifest.GetKeys(); } std::vector Module::GetRegisteredServices() const { std::vector sr; std::vector res; d->coreCtx->services.GetRegisteredByModule(d, sr); for (std::vector::const_iterator i = sr.begin(); i != sr.end(); ++i) { res.push_back(i->GetReference()); } return res; } std::vector Module::GetServicesInUse() const { std::vector sr; std::vector res; d->coreCtx->services.GetUsedByModule(const_cast(this), sr); for (std::vector::const_iterator i = sr.begin(); i != sr.end(); ++i) { res.push_back(i->GetReference()); } return res; } ModuleResource Module::GetResource(const std::string& path) const { - if (d->resourceTreePtrs.empty()) + if (!d->resourceContainer.IsValid()) { return ModuleResource(); } - - for (std::size_t i = 0; i < d->resourceTreePtrs.size(); ++i) - { - if (!d->resourceTreePtrs[i]->IsValid()) continue; - ModuleResource result(path, d->resourceTreePtrs[i], d->resourceTreePtrs); - if (result) return result; - } + ModuleResource result(path, d->resourceContainer); + if (result) return result; return ModuleResource(); } std::vector Module::FindResources(const std::string& path, const std::string& filePattern, bool recurse) const { std::vector result; - if (d->resourceTreePtrs.empty()) return result; - - for (std::size_t i = 0; i < d->resourceTreePtrs.size(); ++i) + if (!d->resourceContainer.IsValid()) { - if (!d->resourceTreePtrs[i]->IsValid()) continue; - - std::vector nodes; - d->resourceTreePtrs[i]->FindNodes(path, filePattern, recurse, nodes); - for (std::vector::iterator nodeIter = nodes.begin(); - nodeIter != nodes.end(); ++nodeIter) - { - result.push_back(ModuleResource(*nodeIter, d->resourceTreePtrs[i], d->resourceTreePtrs)); - } + return result; } + + std::string normalizedPath = path; + // add a leading and trailing slash + if (normalizedPath.empty()) normalizedPath.push_back('/'); + if (*normalizedPath.begin() != '/') normalizedPath = '/' + normalizedPath; + if (*normalizedPath.rbegin() != '/') normalizedPath.push_back('/'); + d->resourceContainer.FindNodes(d->info.name + normalizedPath, + filePattern.empty() ? "*" : filePattern, + recurse, result); return result; } US_END_NAMESPACE US_USE_NAMESPACE std::ostream& operator<<(std::ostream& os, const Module& module) { os << "Module[" << "id=" << module.GetModuleId() << ", loc=" << module.GetLocation() << ", name=" << module.GetName() << "]"; return os; } std::ostream& operator<<(std::ostream& os, Module const * module) { return operator<<(os, *module); } diff --git a/Core/CppMicroServices/core/src/module/usModuleContext.cpp b/Core/CppMicroServices/core/src/module/usModuleContext.cpp index 3f20f7ed21..8f4aa9c2b6 100644 --- a/Core/CppMicroServices/core/src/module/usModuleContext.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleContext.cpp @@ -1,173 +1,196 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleContext.h" #include "usModuleRegistry.h" #include "usModulePrivate.h" +#include "usModuleSettings.h" #include "usCoreModuleContext_p.h" #include "usServiceRegistry_p.h" #include "usServiceReferenceBasePrivate.h" +#include + US_BEGIN_NAMESPACE class ModuleContextPrivate { public: ModuleContextPrivate(ModulePrivate* module) : module(module) {} ModulePrivate* module; }; ModuleContext::ModuleContext(ModulePrivate* module) : d(new ModuleContextPrivate(module)) {} ModuleContext::~ModuleContext() { delete d; } Module* ModuleContext::GetModule() const { return d->module->q; } Module* ModuleContext::GetModule(long id) const { return d->module->coreCtx->moduleHooks.FilterModule(this, ModuleRegistry::GetModule(id)); } -Module*ModuleContext::GetModule(const std::string& name) +Module* ModuleContext::GetModule(const std::string& name) { return ModuleRegistry::GetModule(name); } std::vector ModuleContext::GetModules() const { std::vector modules = ModuleRegistry::GetModules(); d->module->coreCtx->moduleHooks.FilterModules(this, modules); return modules; } ServiceRegistrationU ModuleContext::RegisterService(const InterfaceMap& service, const ServiceProperties& properties) { return d->module->coreCtx->services.RegisterService(d->module, service, properties); } std::vector ModuleContext::GetServiceReferences(const std::string& clazz, const std::string& filter) { std::vector result; std::vector refs; d->module->coreCtx->services.Get(clazz, filter, d->module, refs); for (std::vector::const_iterator iter = refs.begin(); iter != refs.end(); ++iter) { result.push_back(ServiceReferenceU(*iter)); } return result; } ServiceReferenceU ModuleContext::GetServiceReference(const std::string& clazz) { return d->module->coreCtx->services.Get(d->module, clazz); } void* ModuleContext::GetService(const ServiceReferenceBase& reference) { if (!reference) { throw std::invalid_argument("Default constructed ServiceReference is not a valid input to GetService()"); } return reference.d->GetService(d->module->q); } InterfaceMap ModuleContext::GetService(const ServiceReferenceU& reference) { if (!reference) { throw std::invalid_argument("Default constructed ServiceReference is not a valid input to GetService()"); } return reference.d->GetServiceInterfaceMap(d->module->q); } bool ModuleContext::UngetService(const ServiceReferenceBase& reference) { ServiceReferenceBase ref = reference; return ref.d->UngetService(d->module->q, true); } void ModuleContext::AddServiceListener(const ServiceListener& delegate, const std::string& filter) { d->module->coreCtx->listeners.AddServiceListener(this, delegate, NULL, filter); } void ModuleContext::RemoveServiceListener(const ServiceListener& delegate) { d->module->coreCtx->listeners.RemoveServiceListener(this, delegate, NULL); } void ModuleContext::AddModuleListener(const ModuleListener& delegate) { d->module->coreCtx->listeners.AddModuleListener(this, delegate, NULL); } void ModuleContext::RemoveModuleListener(const ModuleListener& delegate) { d->module->coreCtx->listeners.RemoveModuleListener(this, delegate, NULL); } void ModuleContext::AddServiceListener(const ServiceListener& delegate, void* data, const std::string &filter) { d->module->coreCtx->listeners.AddServiceListener(this, delegate, data, filter); } void ModuleContext::RemoveServiceListener(const ServiceListener& delegate, void* data) { d->module->coreCtx->listeners.RemoveServiceListener(this, delegate, data); } void ModuleContext::AddModuleListener(const ModuleListener& delegate, void* data) { d->module->coreCtx->listeners.AddModuleListener(this, delegate, data); } void ModuleContext::RemoveModuleListener(const ModuleListener& delegate, void* data) { d->module->coreCtx->listeners.RemoveModuleListener(this, delegate, data); } std::string ModuleContext::GetDataFile(const std::string &filename) const { - if (d->module->storagePath.empty()) return std::string(); + // compute the module storage path +#ifdef US_PLATFORM_WINDOWS + static const char separator = '\\'; +#else + static const char separator = '/'; +#endif + + std::string baseStoragePath = ModuleSettings::GetStoragePath(); + if (baseStoragePath.empty()) return std::string(); + if (baseStoragePath != d->module->baseStoragePath) + { + d->module->baseStoragePath = baseStoragePath; + d->module->storagePath.clear(); + } + + if (d->module->storagePath.empty()) + { + char buf[50]; + sprintf(buf, "%ld", d->module->info.id); + d->module->storagePath = baseStoragePath + separator + buf + "_" + d->module->info.name + separator; + } return d->module->storagePath + filename; } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleEvent.cpp b/Core/CppMicroServices/core/src/module/usModuleEvent.cpp index bad71db5ad..abf57ccc38 100644 --- a/Core/CppMicroServices/core/src/module/usModuleEvent.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleEvent.cpp @@ -1,121 +1,119 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleEvent.h" #include "usModule.h" US_BEGIN_NAMESPACE class ModuleEventData : public SharedData { public: ModuleEventData(ModuleEvent::Type type, Module* module) : type(type), module(module) { } ModuleEventData(const ModuleEventData& other) : SharedData(other), type(other.type), module(other.module) { } const ModuleEvent::Type type; Module* const module; private: // purposely not implemented ModuleEventData& operator=(const ModuleEventData&); }; ModuleEvent::ModuleEvent() : d(0) { } ModuleEvent::~ModuleEvent() { } bool ModuleEvent::IsNull() const { return !d; } ModuleEvent::ModuleEvent(Type type, Module* module) : d(new ModuleEventData(type, module)) { } ModuleEvent::ModuleEvent(const ModuleEvent& other) : d(other.d) { } ModuleEvent& ModuleEvent::operator=(const ModuleEvent& other) { d = other.d; return *this; } Module* ModuleEvent::GetModule() const { return d->module; } ModuleEvent::Type ModuleEvent::GetType() const { return d->type; } -US_END_NAMESPACE - -US_USE_NAMESPACE - std::ostream& operator<<(std::ostream& os, ModuleEvent::Type eventType) { switch (eventType) { case ModuleEvent::LOADED: return os << "LOADED"; case ModuleEvent::UNLOADED: return os << "UNLOADED"; case ModuleEvent::LOADING: return os << "LOADING"; case ModuleEvent::UNLOADING: return os << "UNLOADING"; default: return os << "Unknown module event type (" << static_cast(eventType) << ")"; } } std::ostream& operator<<(std::ostream& os, const ModuleEvent& event) { if (event.IsNull()) return os << "NONE"; Module* m = event.GetModule(); os << event.GetType() << " #" << m->GetModuleId() << " (" << m->GetLocation() << ")"; return os; } + +US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleInfo.cpp b/Core/CppMicroServices/core/src/module/usModuleInfo.cpp index 804c133c8a..90334976e5 100644 --- a/Core/CppMicroServices/core/src/module/usModuleInfo.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleInfo.cpp @@ -1,34 +1,33 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleInfo.h" US_BEGIN_NAMESPACE -ModuleInfo::ModuleInfo(const std::string& name, const std::string& libName) +ModuleInfo::ModuleInfo(const std::string& name) : name(name) - , libName(libName) , id(0) - , activatorHook(NULL) -{} +{ +} US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleManifest.cpp b/Core/CppMicroServices/core/src/module/usModuleManifest.cpp index 7a2dde83c2..97260243bb 100644 --- a/Core/CppMicroServices/core/src/module/usModuleManifest.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleManifest.cpp @@ -1,142 +1,154 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleManifest_p.h" +#include "jsoncpp.h" + #include US_BEGIN_NAMESPACE +namespace { + + typedef std::map AnyMap; + typedef std::vector AnyVector; + + void ParseJsonObject(const Json::Value& jsonObject, AnyMap& anyMap); + void ParseJsonArray(const Json::Value& jsonArray, AnyVector& anyVector); + + Any ParseJsonValue(const Json::Value& jsonValue) + { + if (jsonValue.isObject()) + { + Any any = AnyMap(); + ParseJsonObject(jsonValue, ref_any_cast(any)); + return any; + } + else if (jsonValue.isArray()) + { + Any any = AnyVector(); + ParseJsonArray(jsonValue, ref_any_cast(any)); + return any; + } + else if (jsonValue.isString()) + { + return Any(jsonValue.asString()); + } + else if (jsonValue.isBool()) + { + return Any(jsonValue.asBool()); + } + else if (jsonValue.isDouble()) + { + return Any(jsonValue.asDouble()); + } + else if (jsonValue.isIntegral()) + { + return Any(jsonValue.asInt()); + } + + return Any(); + } + + void ParseJsonObject(const Json::Value& jsonObject, AnyMap& anyMap) + { + for (Json::Value::const_iterator it = jsonObject.begin(); + it != jsonObject.end(); ++it) + { + const Json::Value& jsonValue = *it; + Any anyValue = ParseJsonValue(jsonValue); + if (!anyValue.Empty()) + { + anyMap.insert(std::make_pair(it.memberName(), anyValue)); + } + } + } + + void ParseJsonArray(const Json::Value& jsonArray, AnyVector& anyVector) + { + for (Json::Value::const_iterator it = jsonArray.begin(); + it != jsonArray.end(); ++it) + { + const Json::Value& jsonValue = *it; + Any anyValue = ParseJsonValue(jsonValue); + if (!anyValue.Empty()) + { + anyVector.push_back(anyValue); + } + } + } + +} + ModuleManifest::ModuleManifest() { } void ModuleManifest::Parse(std::istream& is) { Json::Value root; Json::Reader jsonReader(Json::Features::strictMode()); if (!jsonReader.parse(is, root, false)) { throw std::runtime_error(jsonReader.getFormattedErrorMessages()); } if (!root.isObject()) { throw std::runtime_error("The Json root element must be an object."); } ParseJsonObject(root, m_Properties); } -Any ModuleManifest::ParseJsonValue(const Json::Value& jsonValue) -{ - if (jsonValue.isObject()) - { - Any any = AnyMap(); - ParseJsonObject(jsonValue, ref_any_cast(any)); - return any; - } - else if (jsonValue.isArray()) - { - Any any = AnyVector(); - ParseJsonArray(jsonValue, ref_any_cast(any)); - return any; - } - else if (jsonValue.isString()) - { - return Any(jsonValue.asString()); - } - else if (jsonValue.isBool()) - { - return Any(jsonValue.asBool()); - } - else if (jsonValue.isDouble()) - { - return Any(jsonValue.asDouble()); - } - else if (jsonValue.isIntegral()) - { - return Any(jsonValue.asInt()); - } - - return Any(); -} - -void ModuleManifest::ParseJsonObject(const Json::Value& jsonObject, std::map& anyMap) -{ - for (Json::Value::const_iterator it = jsonObject.begin(); - it != jsonObject.end(); ++it) - { - const Json::Value& jsonValue = *it; - Any anyValue = ParseJsonValue(jsonValue); - if (!anyValue.Empty()) - { - anyMap.insert(std::make_pair(it.memberName(), anyValue)); - } - } -} - -void ModuleManifest::ParseJsonArray(const Json::Value& jsonArray, ModuleManifest::AnyVector& anyVector) -{ - for (Json::Value::const_iterator it = jsonArray.begin(); - it != jsonArray.end(); ++it) - { - const Json::Value& jsonValue = *it; - Any anyValue = ParseJsonValue(jsonValue); - if (!anyValue.Empty()) - { - anyVector.push_back(anyValue); - } - } -} - bool ModuleManifest::Contains(const std::string& key) const { return m_Properties.count(key) > 0; } Any ModuleManifest::GetValue(const std::string& key) const { AnyMap::const_iterator iter = m_Properties.find(key); if (iter != m_Properties.end()) { return iter->second; } return Any(); } std::vector ModuleManifest::GetKeys() const { std::vector keys; for (AnyMap::const_iterator iter = m_Properties.begin(); iter != m_Properties.end(); ++iter) { keys.push_back(iter->first); } return keys; } void ModuleManifest::SetValue(const std::string& key, const Any& value) { m_Properties[key] = value; } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleManifest_p.h b/Core/CppMicroServices/core/src/module/usModuleManifest_p.h index 670094b070..71d54e7136 100644 --- a/Core/CppMicroServices/core/src/module/usModuleManifest_p.h +++ b/Core/CppMicroServices/core/src/module/usModuleManifest_p.h @@ -1,62 +1,54 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEMANIFEST_P_H #define USMODULEMANIFEST_P_H #include "usAny.h" -#include "json_p.h" - US_BEGIN_NAMESPACE class ModuleManifest { public: ModuleManifest(); void Parse(std::istream& is); bool Contains(const std::string& key) const; Any GetValue(const std::string& key) const; std::vector GetKeys() const; void SetValue(const std::string& key, const Any& value); private: typedef std::map AnyMap; - typedef std::vector AnyVector; - - Any ParseJsonValue(const Json::Value& jsonValue); - - void ParseJsonObject(const Json::Value& jsonObject, AnyMap& anyMap); - void ParseJsonArray(const Json::Value& jsonArray, AnyVector& anyVector); AnyMap m_Properties; }; US_END_NAMESPACE #endif // USMODULEMANIFEST_P_H diff --git a/Core/CppMicroServices/core/src/module/usModulePrivate.cpp b/Core/CppMicroServices/core/src/module/usModulePrivate.cpp index 5bd101e306..1e00b80915 100644 --- a/Core/CppMicroServices/core/src/module/usModulePrivate.cpp +++ b/Core/CppMicroServices/core/src/module/usModulePrivate.cpp @@ -1,286 +1,148 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModulePrivate.h" #include "usModule.h" #include "usModuleContext.h" #include "usModuleActivator.h" #include "usModuleUtils_p.h" #include "usModuleSettings.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" #include "usCoreModuleContext_p.h" #include "usServiceRegistration.h" #include "usServiceReferenceBasePrivate.h" #include #include #include #include US_BEGIN_NAMESPACE AtomicInt ModulePrivate::idCounter; ModulePrivate::ModulePrivate(Module* qq, CoreModuleContext* coreCtx, ModuleInfo* info) : coreCtx(coreCtx) , info(*info) + , resourceContainer(info) , moduleContext(0) , moduleActivator(0) , q(qq) { - // Parse the statically imported module library names - typedef const char*(*GetImportedModulesFunc)(void); - - std::string getImportedModulesSymbol("_us_get_imported_modules_for_"); - getImportedModulesSymbol += this->info.libName; - - std::string location = this->info.location; - if (this->info.libName.empty()) - { - /* make sure we retrieve symbols from the executable, if "libName" is empty */ - location.clear(); - } - - GetImportedModulesFunc getImportedModulesFunc = NULL; - void* getImportedModulesSym = ModuleUtils::GetSymbol(location, getImportedModulesSymbol.c_str()); - std::memcpy(&getImportedModulesFunc, &getImportedModulesSym, sizeof(void*)); - if (getImportedModulesFunc != NULL) - { - std::string importedStaticModuleLibNames = getImportedModulesFunc(); - - std::istringstream iss(importedStaticModuleLibNames); - std::copy(std::istream_iterator(iss), std::istream_iterator(), - std::back_inserter >(this->staticModuleLibNames)); - } - - InitializeResources(location); - // Check if the module provides a manifest.json file and if yes, parse it. - ModuleResource manifestRes; - std::map::iterator resourceTreeIter = mapLibNameToResourceTrees.find(this->info.libName); - if (resourceTreeIter != mapLibNameToResourceTrees.end() && resourceTreeIter->second->IsValid()) + if (resourceContainer.IsValid()) { - manifestRes = ModuleResource("/manifest.json", resourceTreeIter->second, resourceTreePtrs); + ModuleResource manifestRes("/manifest.json", resourceContainer); if (manifestRes) { ModuleResourceStream manifestStream(manifestRes); try { moduleManifest.Parse(manifestStream); } catch (const std::exception& e) { US_ERROR << "Parsing of manifest.json for module " << info->location << " failed: " << e.what(); } } } // Check if we got version information and validate the version identifier if (moduleManifest.Contains(Module::PROP_VERSION())) { Any versionAny = moduleManifest.GetValue(Module::PROP_VERSION()); std::string errMsg; if (versionAny.Type() != typeid(std::string)) { errMsg = std::string("The version identifier must be a string"); } try { version = ModuleVersion(versionAny.ToString()); } catch (const std::exception& e) { errMsg = std::string("The version identifier is invalid: ") + e.what(); } if (!errMsg.empty()) { throw std::invalid_argument(std::string("The Json value for ") + Module::PROP_VERSION() + " for module " + info->location + " is not valid: " + errMsg); } } std::stringstream propId; propId << this->info.id; moduleManifest.SetValue(Module::PROP_ID(), propId.str()); moduleManifest.SetValue(Module::PROP_LOCATION(), this->info.location); moduleManifest.SetValue(Module::PROP_NAME(), this->info.name); if (moduleManifest.Contains(Module::PROP_AUTOLOAD_DIR())) { this->info.autoLoadDir = moduleManifest.GetValue(Module::PROP_AUTOLOAD_DIR()).ToString(); } else { - // default to the library name or a special name for executables - if (!this->info.libName.empty()) - { - this->info.autoLoadDir = this->info.libName; - moduleManifest.SetValue(Module::PROP_AUTOLOAD_DIR(), Any(this->info.autoLoadDir)); - } - else - { - this->info.autoLoadDir = "main"; - moduleManifest.SetValue(Module::PROP_AUTOLOAD_DIR(), Any(this->info.autoLoadDir)); - } - } - - // comput the module storage path -#ifdef US_PLATFORM_WINDOWS - static const char separator = '\\'; -#else - static const char separator = '/'; -#endif - - std::string baseStoragePath = ModuleSettings::GetStoragePath(); - if (!baseStoragePath.empty()) - { - char buf[50]; - sprintf(buf, "%ld", this->info.id); - storagePath = baseStoragePath + separator + buf + "_" + this->info.libName + separator; + this->info.autoLoadDir = this->info.name; + moduleManifest.SetValue(Module::PROP_AUTOLOAD_DIR(), Any(this->info.autoLoadDir)); } } ModulePrivate::~ModulePrivate() { delete moduleContext; - - for (std::size_t i = 0; i < this->resourceTreePtrs.size(); ++i) - { - delete resourceTreePtrs[i]; - } } void ModulePrivate::RemoveModuleResources() { coreCtx->listeners.RemoveAllListeners(moduleContext); std::vector srs; coreCtx->services.GetRegisteredByModule(this, srs); for (std::vector::iterator i = srs.begin(); i != srs.end(); ++i) { try { i->Unregister(); } catch (const std::logic_error& /*ignore*/) { // Someone has unregistered the service after stop completed. // This should not occur, but we don't want get stuck in // an illegal state so we catch it. } } srs.clear(); coreCtx->services.GetUsedByModule(q, srs); for (std::vector::const_iterator i = srs.begin(); i != srs.end(); ++i) { i->GetReference(std::string()).d->UngetService(q, false); } - - for (std::size_t i = 0; i < resourceTreePtrs.size(); ++i) - { - resourceTreePtrs[i]->Invalidate(); - } -} - -void ModulePrivate::StartStaticModules() -{ - std::string location = this->info.location; - if (this->info.libName.empty()) - { - /* make sure we retrieve symbols from the executable, if "libName" is empty */ - location.clear(); - } - - for (std::vector::iterator i = staticModuleLibNames.begin(); - i != staticModuleLibNames.end(); ++i) - { - std::string staticActivatorSymbol = "_us_module_activator_instance_"; - staticActivatorSymbol += *i; - ModuleInfo::ModuleActivatorHook staticActivator = NULL; - void* staticActivatorSym = ModuleUtils::GetSymbol(location, staticActivatorSymbol.c_str()); - std::memcpy(&staticActivator, &staticActivatorSym, sizeof(void*)); - if (staticActivator) - { - US_DEBUG << "Loading static activator " << *i; - staticActivators.push_back(staticActivator); - staticActivator()->Load(moduleContext); - } - else - { - US_DEBUG << "Could not find an activator for the static module " << (*i) - << ". It propably does not provide an activator on purpose.\n Or you either " - "forgot a US_IMPORT_MODULE macro call in " << info.libName - << " or to link " << (*i) << " to " << info.libName << "."; - } - } - -} - -void ModulePrivate::StopStaticModules() -{ - for (std::list::iterator i = staticActivators.begin(); - i != staticActivators.end(); ++i) - { - (*i)()->Unload(moduleContext); - } -} - -void ModulePrivate::InitializeResources(const std::string& location) -{ - // Get the resource data from static modules and this module - std::vector moduleLibNames; - moduleLibNames.push_back(this->info.libName); - moduleLibNames.insert(moduleLibNames.end(), - this->staticModuleLibNames.begin(), this->staticModuleLibNames.end()); - - std::string initResourcesSymbolPrefix = "_us_init_resources_"; - for (std::size_t i = 0; i < moduleLibNames.size(); ++i) - { - std::string initResourcesSymbol = initResourcesSymbolPrefix + moduleLibNames[i]; - ModuleInfo::InitResourcesHook initResourcesFunc = NULL; - void* initResourcesSym = ModuleUtils::GetSymbol(location, initResourcesSymbol.c_str()); - std::memcpy(&initResourcesFunc, &initResourcesSym, sizeof(void*)); - if (initResourcesFunc) - { - initResourcesFunc(&this->info); - } - } - - // Initialize this modules resource trees - assert(this->info.resourceData.size() == this->info.resourceNames.size()); - assert(this->info.resourceNames.size() == this->info.resourceTree.size()); - for (std::size_t i = 0; i < this->info.resourceData.size(); ++i) - { - resourceTreePtrs.push_back(new ModuleResourceTree(this->info.resourceTree[i], - this->info.resourceNames[i], - this->info.resourceData[i])); - mapLibNameToResourceTrees[moduleLibNames[i]] = resourceTreePtrs.back(); - } } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModulePrivate.h b/Core/CppMicroServices/core/src/module/usModulePrivate.h index a292122154..7ef4ff773d 100644 --- a/Core/CppMicroServices/core/src/module/usModulePrivate.h +++ b/Core/CppMicroServices/core/src/module/usModulePrivate.h @@ -1,105 +1,97 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEPRIVATE_H #define USMODULEPRIVATE_H #include #include #include "usModuleRegistry.h" #include "usModuleVersion.h" #include "usModuleInfo.h" #include "usModuleManifest_p.h" -#include "usModuleResourceTree_p.h" +#include "usModuleResourceContainer_p.h" #include "usAtomicInt_p.h" US_BEGIN_NAMESPACE class CoreModuleContext; class ModuleContext; struct ModuleActivator; /** * \ingroup MicroServices */ class ModulePrivate { public: /** * Construct a new module based on a ModuleInfo object. */ ModulePrivate(Module* qq, CoreModuleContext* coreCtx, ModuleInfo* info); virtual ~ModulePrivate(); void RemoveModuleResources(); - void StartStaticModules(); - - void StopStaticModules(); - CoreModuleContext* const coreCtx; - std::vector staticModuleLibNames; - /** * Module version */ ModuleVersion version; ModuleInfo info; - std::vector resourceTreePtrs; - std::map mapLibNameToResourceTrees; + ModuleResourceContainer resourceContainer; /** * ModuleContext for the module */ ModuleContext* moduleContext; ModuleActivator* moduleActivator; ModuleManifest moduleManifest; + std::string baseStoragePath; std::string storagePath; Module* const q; private: - void InitializeResources(const std::string& location); - - std::list staticActivators; + void InitializeResources(); static AtomicInt idCounter; // purposely not implemented ModulePrivate(const ModulePrivate&); ModulePrivate& operator=(const ModulePrivate&); }; US_END_NAMESPACE #endif // USMODULEPRIVATE_H diff --git a/Core/CppMicroServices/core/src/module/usModuleRegistry.cpp b/Core/CppMicroServices/core/src/module/usModuleRegistry.cpp index f50d9fdac0..1865819e20 100644 --- a/Core/CppMicroServices/core/src/module/usModuleRegistry.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleRegistry.cpp @@ -1,221 +1,224 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleRegistry.h" #include "usModule.h" #include "usModuleInfo.h" #include "usModuleContext.h" #include "usModuleActivator.h" +#include "usModuleInitialization.h" #include "usCoreModuleContext_p.h" #include "usGetModuleContext.h" #include "usStaticInit_p.h" #include #include US_BEGIN_NAMESPACE -typedef US_UNORDERED_MAP_TYPE ModuleMap; +typedef US_UNORDERED_MAP_TYPE ModuleMap; US_GLOBAL_STATIC(CoreModuleContext, coreModuleContext) template struct ModuleDeleter { void operator()(GlobalStatic& globalStatic) const { ModuleMap* moduleMap = globalStatic.pointer; for (ModuleMap::const_iterator i = moduleMap->begin(); i != moduleMap->end(); ++i) { delete i->second; } DefaultGlobalStaticDeleter defaultDeleter; defaultDeleter(globalStatic); } }; /** * Table of all installed modules in this framework. * Key is the module id. */ US_GLOBAL_STATIC_WITH_DELETER(ModuleMap, modules, ModuleDeleter) /** * Lock for protecting the modules object */ US_GLOBAL_STATIC(Mutex, modulesLock) /** * Lock for protecting the register count */ US_GLOBAL_STATIC(Mutex, countLock) - void ModuleRegistry::Register(ModuleInfo* info) { static long regCount = 0; if (info->id > 0) { // The module was already registered Module* module = 0; { MutexLock lock(*modulesLock()); - module = modules()->operator[](info->id); + module = modules()->operator[](info->name); assert(module != 0); } module->Start(); } else { Module* module = 0; // check if the module is reloaded { MutexLock lock(*modulesLock()); ModuleMap* map = modules(); for (ModuleMap::const_iterator i = map->begin(); i != map->end(); ++i) { - if (i->second->GetLocation() == info->location) + if (i->second->GetLocation() == info->location && + i->second->GetName() == info->name) { module = i->second; info->id = module->GetModuleId(); } } } if (!module) { module = new Module(); countLock()->Lock(); info->id = ++regCount; + assert(info->id == 1 ? info->name == "CppMicroServices" : true); countLock()->Unlock(); module->Init(coreModuleContext(), info); MutexLock lock(*modulesLock()); ModuleMap* map = modules(); - map->insert(std::make_pair(info->id, module)); + map->insert(std::make_pair(info->name, module)); } else { module->Init(coreModuleContext(), info); } module->Start(); } } void ModuleRegistry::UnRegister(const ModuleInfo* info) { - // If we are unregistering the core module, we just call - // the module activators Unload() method (if there is a - // module activator). Since we cannot be sure that the - // ModuleContext for the core library is still valid, we - // just pass a null-pointer. Using the module context during - // static deinitalization time of the core library makes - // no sense anyway. - if (info->id == 1) + if (info->id > 1) { - // Remove listeners from static modules if they have forgotten to do so - coreModuleContext()->listeners.RemoveAllListeners(GetModuleContext()); - - if (info->activatorHook) + Module* curr = 0; { - info->activatorHook()->Unload(0); + MutexLock lock(*modulesLock()); + curr = modules()->operator[](info->name); + assert(curr != 0); } - return; - } - - Module* curr = 0; - { - MutexLock lock(*modulesLock()); - curr = modules()->operator[](info->id); - assert(curr != 0); + curr->Stop(); } - - curr->Stop(); } Module* ModuleRegistry::GetModule(long id) { MutexLock lock(*modulesLock()); - ModuleMap::const_iterator iter = modules()->find(id); - if (iter != modules()->end()) + ModuleMap::const_iterator iter = modules()->begin(); + ModuleMap::const_iterator iterEnd = modules()->end(); + for (; iter != iterEnd; ++iter) { - return iter->second; + if (iter->second->GetModuleId() == id) + { + return iter->second; + } } return 0; } Module* ModuleRegistry::GetModule(const std::string& name) { MutexLock lock(*modulesLock()); - ModuleMap::const_iterator iter = modules()->begin(); - ModuleMap::const_iterator iterEnd = modules()->end(); - for (; iter != iterEnd; ++iter) + ModuleMap::const_iterator iter = modules()->find(name); + if (iter != modules()->end()) { - if (iter->second->GetName() == name) - { - return iter->second; - } + return iter->second; } - return 0; } std::vector ModuleRegistry::GetModules() { MutexLock lock(*modulesLock()); std::vector result; ModuleMap* map = modules(); ModuleMap::const_iterator iter = map->begin(); ModuleMap::const_iterator iterEnd = map->end(); for (; iter != iterEnd; ++iter) { result.push_back(iter->second); } return result; } std::vector ModuleRegistry::GetLoadedModules() { MutexLock lock(*modulesLock()); std::vector result; ModuleMap::const_iterator iter = modules()->begin(); ModuleMap::const_iterator iterEnd = modules()->end(); for (; iter != iterEnd; ++iter) { if (iter->second->IsLoaded()) { result.push_back(iter->second); } } return result; } +// Control the static initialization order for several core objects +struct StaticInitializationOrder +{ + StaticInitializationOrder() + { + ModuleSettings::GetLogLevel(); + modulesLock(); + countLock(); + modules(); + coreModuleContext(); + } +}; + +static StaticInitializationOrder _staticInitializationOrder; + US_END_NAMESPACE + +// We initialize the CppMicroService module after making sure +// that all other global statics have been initialized above +US_INITIALIZE_MODULE diff --git a/Core/CppMicroServices/core/src/module/usModuleResource.cpp b/Core/CppMicroServices/core/src/module/usModuleResource.cpp index 35cb0eb378..dd798238dc 100644 --- a/Core/CppMicroServices/core/src/module/usModuleResource.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleResource.cpp @@ -1,271 +1,289 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleResource.h" #include "usAtomicInt_p.h" -#include "usModuleResourceTree_p.h" +#include "usModuleResourceContainer_p.h" +#include "usModuleInfo.h" #include US_BEGIN_NAMESPACE class ModuleResourcePrivate { public: - ModuleResourcePrivate() - : associatedResourceTree(NULL) - , node(-1) - , size(0) - , data(NULL) - , isFile(false) - , isCompressed(false) + ModuleResourcePrivate(const ModuleResourceContainer* rc) + : resourceContainer(rc) , ref(1) {} - std::string fileName; - std::string path; - std::string filePath; + void InitFilePath(const std::string& file); - std::vector resourceTrees; - const ModuleResourceTree* associatedResourceTree; + const ModuleResourceContainer* const resourceContainer; - int node; - int32_t size; - const unsigned char* data; - unsigned char* uncompressedData; + ModuleResourceContainer::Stat stat; - mutable std::vector children; + std::string fileName; + std::string path; - bool isFile; - bool isCompressed; + mutable std::vector children; + mutable std::vector childNodes; /** * Reference count for implicitly shared private implementation. */ AtomicInt ref; }; -ModuleResource::ModuleResource() - : d(new ModuleResourcePrivate) -{ -} - -ModuleResource::ModuleResource(const ModuleResource &resource) - : d(resource.d) +void ModuleResourcePrivate::InitFilePath(const std::string& file) { - d->ref.Ref(); -} - -ModuleResource::ModuleResource(const std::string& _file, ModuleResourceTree* associatedResourceTree, - const std::vector& resourceTrees) - : d(new ModuleResourcePrivate) -{ - d->resourceTrees = resourceTrees; - d->associatedResourceTree = associatedResourceTree; - - std::string file = _file; - if (file.empty()) file = "/"; - if (file[0] != '/') file = std::string("/") + file; + std::string normalizedFile = file; + if (normalizedFile.empty() || normalizedFile[0] != '/') + { + normalizedFile = '/' + normalizedFile; + } - std::size_t index = file.find_last_of('/'); - if (index < file.size()-1) + std::string rawPath; + std::size_t index = normalizedFile.find_last_of('/'); + if (index == std::string::npos) + { + fileName = normalizedFile; + } + else if (index < normalizedFile.size()-1) + { + fileName = normalizedFile.substr(index+1); + rawPath = normalizedFile.substr(0,index+1); + } + else { - d->fileName = file.substr(index+1); + rawPath = normalizedFile; } - std::string rawPath = file.substr(0,index+1); // remove duplicate / std::string::value_type lastChar = 0; for (std::size_t i = 0; i < rawPath.size(); ++i) { if (rawPath[i] == '/' && lastChar == '/') { continue; } lastChar = rawPath[i]; - d->path.push_back(lastChar); + path.push_back(lastChar); } - - d->filePath = d->path + d->fileName; - - d->node = d->associatedResourceTree->FindNode(GetResourcePath()); - if (d->node != -1) + if (path.empty()) { - d->isFile = !d->associatedResourceTree->IsDir(d->node); - if (d->isFile) - { - d->data = d->associatedResourceTree->GetData(d->node, &d->size); - d->isCompressed = d->associatedResourceTree->IsCompressed(d->node); - } + path.push_back('/'); } } +ModuleResource::ModuleResource() + : d(new ModuleResourcePrivate(NULL)) +{ +} + +ModuleResource::ModuleResource(const ModuleResource &resource) + : d(resource.d) +{ + d->ref.Ref(); +} + +ModuleResource::ModuleResource(const std::string& file, const ModuleResourceContainer& resourceContainer) + : d(new ModuleResourcePrivate(&resourceContainer)) +{ + d->InitFilePath(file); + + d->stat.filePath = d->resourceContainer->GetModuleInfo()->name + d->path + d->fileName; + + d->resourceContainer->GetStat(d->stat); +} + +ModuleResource::ModuleResource(int index, const ModuleResourceContainer& resourceContainer) + : d(new ModuleResourcePrivate(&resourceContainer)) +{ + d->resourceContainer->GetStat(index, d->stat); + d->InitFilePath(d->stat.filePath.substr(d->resourceContainer->GetModuleInfo()->name.size())); +} + ModuleResource::~ModuleResource() { if (!d->ref.Deref()) delete d; } ModuleResource& ModuleResource::operator =(const ModuleResource& resource) { ModuleResourcePrivate* curr_d = d; d = resource.d; d->ref.Ref(); if (!curr_d->ref.Deref()) delete curr_d; return *this; } bool ModuleResource::operator <(const ModuleResource& resource) const { return this->GetResourcePath() < resource.GetResourcePath(); } bool ModuleResource::operator ==(const ModuleResource& resource) const { - return d->associatedResourceTree == resource.d->associatedResourceTree && + return d->resourceContainer == resource.d->resourceContainer && this->GetResourcePath() == resource.GetResourcePath(); } bool ModuleResource::operator !=(const ModuleResource &resource) const { return !(*this == resource); } bool ModuleResource::IsValid() const { - return d->associatedResourceTree && d->associatedResourceTree->IsValid() && d->node > -1; -} - -bool ModuleResource::IsCompressed() const -{ - return d->isCompressed; + return d->resourceContainer && d->resourceContainer->IsValid() && d->stat.index > -1; } ModuleResource::operator bool_type() const { return IsValid() ? &ModuleResource::d : NULL; } std::string ModuleResource::GetName() const { return d->fileName; } std::string ModuleResource::GetPath() const { return d->path; } std::string ModuleResource::GetResourcePath() const { - return d->filePath; + return d->path + d->fileName; } std::string ModuleResource::GetBaseName() const { return d->fileName.substr(0, d->fileName.find_first_of('.')); } std::string ModuleResource::GetCompleteBaseName() const { return d->fileName.substr(0, d->fileName.find_last_of('.')); } std::string ModuleResource::GetSuffix() const { std::size_t index = d->fileName.find_last_of('.'); return index < d->fileName.size()-1 ? d->fileName.substr(index+1) : std::string(""); } std::string ModuleResource::GetCompleteSuffix() const { std::size_t index = d->fileName.find_first_of('.'); return index < d->fileName.size()-1 ? d->fileName.substr(index+1) : std::string(""); } bool ModuleResource::IsDir() const { - return !d->isFile; + return d->stat.isDir; } bool ModuleResource::IsFile() const { - return d->isFile; + return !d->stat.isDir; } std::vector ModuleResource::GetChildren() const { - if (d->isFile || !IsValid()) return d->children; + if (!IsValid() || !IsDir()) return d->children; - if (!d->children.empty()) return d->children; - - bool indexPastAssociatedResTree = false; - for (std::size_t i = 0; i < d->resourceTrees.size(); ++i) + if (d->children.empty()) { - if (d->resourceTrees[i] == d->associatedResourceTree) - { - indexPastAssociatedResTree = true; - d->associatedResourceTree->GetChildren(d->node, d->children); - } - else if (indexPastAssociatedResTree) - { - int nodeIndex = d->resourceTrees[i]->FindNode(GetPath()); - if (nodeIndex > -1) - { - d->resourceTrees[i]->GetChildren(d->node, d->children); - } - } + d->resourceContainer->GetChildren(d->stat.filePath, true, + d->children, d->childNodes); } return d->children; } +std::vector ModuleResource::GetChildResources() const +{ + std::vector childResources; + + if (!IsValid() || !IsDir()) return childResources; + + if (d->childNodes.empty()) + { + d->resourceContainer->GetChildren(this->GetResourcePath(), true, + d->children, d->childNodes); + } + + for (std::vector::const_iterator iter = d->childNodes.begin(), + iterEnd = d->childNodes.end(); iter != iterEnd; ++iter) + { + childResources.push_back(ModuleResource(static_cast(*iter), *d->resourceContainer)); + } + return childResources; +} + int ModuleResource::GetSize() const { - return d->size; + return d->stat.uncompressedSize; } -const unsigned char* ModuleResource::GetData() const +time_t ModuleResource::GetLastModified() const { - if (!IsValid()) return NULL; - return d->data; + return d->stat.modifiedTime; } std::size_t ModuleResource::Hash() const { using namespace US_HASH_FUNCTION_NAMESPACE; - return US_HASH_FUNCTION(std::string, this->GetResourcePath()); + return US_HASH_FUNCTION(std::string, d->resourceContainer->GetModuleInfo()->name + this->GetResourcePath()); +} + +void* ModuleResource::GetData() const +{ + if (!IsValid()) return NULL; + + void* data = d->resourceContainer->GetData(d->stat.index); + if (data == NULL) + { + US_WARN << "Error uncompressing resource data for " << this->GetResourcePath() << " from " + << d->resourceContainer->GetModuleInfo()->location; + } + return data; } US_END_NAMESPACE US_USE_NAMESPACE std::ostream& operator<<(std::ostream& os, const ModuleResource& resource) { return os << resource.GetResourcePath(); } diff --git a/Core/CppMicroServices/core/src/module/usModuleResourceBuffer.cpp b/Core/CppMicroServices/core/src/module/usModuleResourceBuffer.cpp index 1946ebbed4..8f189a2b00 100644 --- a/Core/CppMicroServices/core/src/module/usModuleResourceBuffer.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleResourceBuffer.cpp @@ -1,303 +1,287 @@ -/*=================================================================== +/*============================================================================= -BlueBerry Platform + Library: CppMicroServices -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -See LICENSE.txt or http://www.mitk.org for details. + http://www.apache.org/licenses/LICENSE-2.0 -===================================================================*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ #include "usModuleResourceBuffer_p.h" -#include "usUncompressResourceData.h" #include "us_stdint.h" #include #include +#include #ifdef US_PLATFORM_WINDOWS #define DATA_NEEDS_NEWLINE_CONVERSION 1 #undef REMOVE_LAST_NEWLINE_IN_TEXT_MODE #else #undef DATA_NEEDS_NEWLINE_CONVERSION #define REMOVE_LAST_NEWLINE_IN_TEXT_MODE 1 #endif US_BEGIN_NAMESPACE class ModuleResourceBufferPrivate { public: - ModuleResourceBufferPrivate(const char* begin, std::size_t size, std::ios_base::openmode mode) + ModuleResourceBufferPrivate(void* data, std::size_t size, const char* begin, std::ios_base::openmode mode) : begin(begin) , end(begin + size) , current(begin) , mode(mode) - #ifdef US_ENABLE_RESOURCE_COMPRESSION - , uncompressedData(NULL) - #endif + , uncompressedData(reinterpret_cast(data)) #ifdef DATA_NEEDS_NEWLINE_CONVERSION , pos(0) #endif { } ~ModuleResourceBufferPrivate() { -#ifdef US_ENABLE_RESOURCE_COMPRESSION - delete[] uncompressedData; -#endif + free(uncompressedData); } const char* const begin; const char* const end; const char* current; const std::ios_base::openmode mode; -#ifdef US_ENABLE_RESOURCE_COMPRESSION - const unsigned char* uncompressedData; -#endif + unsigned char* uncompressedData; #ifdef DATA_NEEDS_NEWLINE_CONVERSION // records the stream position ignoring CR characters std::streambuf::pos_type pos; #endif }; -ModuleResourceBuffer::ModuleResourceBuffer(const unsigned char* data, std::size_t _size, - std::ios_base::openmode mode, bool compressed) +ModuleResourceBuffer::ModuleResourceBuffer(void* data, std::size_t _size, + std::ios_base::openmode mode) : d(NULL) { assert(_size < static_cast(std::numeric_limits::max())); // assert(data != NULL); - if (compressed && _size) - { -#ifdef US_ENABLE_RESOURCE_COMPRESSION - data = UncompressResourceData(data, _size, &_size); -#else - assert(!"CppMicroServices built without support for resource compression"); -#endif - } - - const char* begin = reinterpret_cast(data); + char* begin = reinterpret_cast(data); std::size_t size = _size; #ifdef DATA_NEEDS_NEWLINE_CONVERSION if (data != NULL && !(mode & std::ios_base::binary) && begin[0] == '\r') { ++begin; --size; } #endif #ifdef REMOVE_LAST_NEWLINE_IN_TEXT_MODE if (data != NULL && !(mode & std::ios_base::binary) && begin[size-1] == '\n') { --size; } #endif - d = new ModuleResourceBufferPrivate(begin, size, mode); -#ifdef US_ENABLE_RESOURCE_COMPRESSION - if (compressed) - { - d->uncompressedData = data; - } -#endif + d = new ModuleResourceBufferPrivate(data, size, begin, mode); } ModuleResourceBuffer::~ModuleResourceBuffer() { delete d; } ModuleResourceBuffer::int_type ModuleResourceBuffer::underflow() { if (d->current == d->end) return traits_type::eof(); #ifdef DATA_NEEDS_NEWLINE_CONVERSION char c = *d->current; if (!(d->mode & std::ios_base::binary)) { if (c == '\r') { if (d->current + 1 == d->end) { return traits_type::eof(); } c = d->current[1]; } } return traits_type::to_int_type(c); #else return traits_type::to_int_type(*d->current); #endif } ModuleResourceBuffer::int_type ModuleResourceBuffer::uflow() { if (d->current == d->end) return traits_type::eof(); #ifdef DATA_NEEDS_NEWLINE_CONVERSION char c = *d->current++; if (!(d->mode & std::ios_base::binary)) { if (c == '\r') { if (d->current == d->end) { return traits_type::eof(); } c = *d->current++; } } return traits_type::to_int_type(c); #else return traits_type::to_int_type(*d->current++); #endif } ModuleResourceBuffer::int_type ModuleResourceBuffer::pbackfail(int_type ch) { int backOffset = -1; #ifdef DATA_NEEDS_NEWLINE_CONVERSION if (!(d->mode & std::ios_base::binary)) { while ((d->current - backOffset) >= d->begin && d->current[backOffset] == '\r') { --backOffset; } // d->begin always points to a character != '\r' } #endif if (d->current == d->begin || (ch != traits_type::eof() && ch != d->current[backOffset])) { return traits_type::eof(); } d->current += backOffset; return traits_type::to_int_type(*d->current); } std::streamsize ModuleResourceBuffer::showmanyc() { assert(d->current <= d->end); #ifdef DATA_NEEDS_NEWLINE_CONVERSION std::streamsize ssize = 0; std::size_t chunkSize = d->end - d->current; for (std::size_t i = 0; i < chunkSize; ++i) { if (d->current[i] != '\r') { ++ssize; } } return ssize; #else return d->end - d->current; #endif } std::streambuf::pos_type ModuleResourceBuffer::seekoff(std::streambuf::off_type off, std::ios_base::seekdir way, std::ios_base::openmode /*which*/) { #ifdef DATA_NEEDS_NEWLINE_CONVERSION std::streambuf::off_type step = 1; if (way == std::ios_base::beg) { d->current = d->begin; } else if (way == std::ios_base::end) { d->current = d->end; step = -1; } if (!(d->mode & std::ios_base::binary)) { if (way == std::ios_base::beg) { d->pos = 0; } else if (way == std::ios_base::end) { d->current -= 1; } std::streambuf::off_type i = 0; // scan through off amount of characters excluding '\r' while (i != off) { if (*d->current != '\r') { i += step; d->pos += step; } d->current += step; } // adjust the position in case of a "backwards" seek if (way == std::ios_base::end) { // fix pointer from previous while loop d->current += 1; d->pos = 0; i = 0; const std::streampos currInternalPos = d->current - d->begin; while (i != currInternalPos) { if (d->begin[i] != '\r') { d->pos += 1; } ++i; } } } else { d->current += off; d->pos = d->current - d->begin; } return d->pos; #else if (way == std::ios_base::beg) { d->current = d->begin + off; return off; } else if (way == std::ios_base::cur) { d->current += off; return d->current - d->begin; } else { d->current = d->end + off; return d->current - d->begin; } #endif } std::streambuf::pos_type ModuleResourceBuffer::seekpos(std::streambuf::pos_type sp, std::ios_base::openmode /*which*/) { return this->seekoff(sp, std::ios_base::beg); } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleResourceBuffer_p.h b/Core/CppMicroServices/core/src/module/usModuleResourceBuffer_p.h index 0f09c86b33..f98614f8ae 100644 --- a/Core/CppMicroServices/core/src/module/usModuleResourceBuffer_p.h +++ b/Core/CppMicroServices/core/src/module/usModuleResourceBuffer_p.h @@ -1,63 +1,68 @@ -/*=================================================================== +/*============================================================================= -BlueBerry Platform + Library: CppMicroServices -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -See LICENSE.txt or http://www.mitk.org for details. + http://www.apache.org/licenses/LICENSE-2.0 -===================================================================*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ #ifndef USMODULERESOURCEBUFFER_P_H #define USMODULERESOURCEBUFFER_P_H #include #include US_BEGIN_NAMESPACE class ModuleResourceBufferPrivate; class US_Core_EXPORT ModuleResourceBuffer: public std::streambuf { public: - explicit ModuleResourceBuffer(const unsigned char* data, std::size_t size, - std::ios_base::openmode mode, bool compressed); + explicit ModuleResourceBuffer(void* data, std::size_t size, + std::ios_base::openmode mode); ~ModuleResourceBuffer(); private: int_type underflow(); int_type uflow(); int_type pbackfail(int_type ch); std::streamsize showmanyc(); pos_type seekoff (off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out); pos_type seekpos (pos_type sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out); // purposely not implemented ModuleResourceBuffer(const ModuleResourceBuffer&); ModuleResourceBuffer& operator=(const ModuleResourceBuffer&); private: ModuleResourceBufferPrivate* d; }; US_END_NAMESPACE #endif // USMODULERESOURCEBUFFER_P_H diff --git a/Core/CppMicroServices/core/src/module/usModuleResourceContainer.cpp b/Core/CppMicroServices/core/src/module/usModuleResourceContainer.cpp new file mode 100644 index 0000000000..efc9237f6d --- /dev/null +++ b/Core/CppMicroServices/core/src/module/usModuleResourceContainer.cpp @@ -0,0 +1,229 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include "usModuleResourceContainer_p.h" + +#include "usModuleInfo.h" +#include "usModuleUtils_p.h" +#include "usModuleResource.h" +#include "usLog_p.h" + +#include "miniz.h" + +#include +#include +#include +#include + +US_BEGIN_NAMESPACE + +struct ModuleResourceContainerPrivate +{ + ModuleResourceContainerPrivate(const ModuleInfo* moduleInfo) + : m_ModuleInfo(moduleInfo) + , m_IsValid(false) + , m_ZipArchive() + {} + + typedef std::pair NameIndexPair; + + struct PairComp + { + bool operator()(const NameIndexPair& p1, const NameIndexPair& p2) const + { + return p1.first < p2.first; + } + }; + + typedef std::set SetType; + + void InitSortedEntries() + { + if (m_SortedEntries.empty()) + { + mz_uint numFiles = mz_zip_reader_get_num_files(&m_ZipArchive); + for (mz_uint fileIndex = 0; fileIndex < numFiles; ++fileIndex) + { + char fileName[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + mz_zip_reader_get_filename(&m_ZipArchive, fileIndex, fileName, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE); + m_SortedEntries.insert(std::make_pair(std::string(fileName), fileIndex)); + } + } + } + + const ModuleInfo* m_ModuleInfo; + bool m_IsValid; + + mz_zip_archive m_ZipArchive; + + std::set m_SortedEntries; +}; + +ModuleResourceContainer::ModuleResourceContainer(const ModuleInfo* moduleInfo) + : d(new ModuleResourceContainerPrivate(moduleInfo)) +{ + if (mz_zip_reader_init_file(&d->m_ZipArchive, moduleInfo->location.c_str(), 0)) + { + d->m_IsValid = true; + } + else + { + US_DEBUG << "Could not init zip archive for module " << moduleInfo->name; + } +} + +ModuleResourceContainer::~ModuleResourceContainer() +{ + if (IsValid()) + { + mz_zip_reader_end(&d->m_ZipArchive); + } + delete d; +} + +bool ModuleResourceContainer::IsValid() const +{ + return d->m_IsValid; +} + +bool ModuleResourceContainer::GetStat(ModuleResourceContainer::Stat& stat) const +{ + if (IsValid()) + { + int fileIndex = mz_zip_reader_locate_file(&d->m_ZipArchive, stat.filePath.c_str(), NULL, 0); + if (fileIndex >= 0) + { + return GetStat(fileIndex, stat); + } + } + return false; +} + +bool ModuleResourceContainer::GetStat(int index, ModuleResourceContainer::Stat& stat) const +{ + if (IsValid()) + { + if (index >= 0) + { + mz_zip_archive_file_stat zipStat; + if (!mz_zip_reader_file_stat(&d->m_ZipArchive, index, &zipStat)) + { + return false; + } + stat.index = index; + stat.filePath = zipStat.m_filename; + stat.isDir = mz_zip_reader_is_file_a_directory(&d->m_ZipArchive, index) ? true : false; + stat.modifiedTime = zipStat.m_time; + // This will limit the size info from uint64 to uint32 on 32-bit + // architectures. We don't care because we assume resources > 2GB + // don't make sense to be embedded in a module anyway. + assert(zipStat.m_comp_size < INT_MAX); + assert(zipStat.m_uncomp_size < INT_MAX); + stat.uncompressedSize = static_cast(zipStat.m_uncomp_size); + return true; + } + } + return false; +} + +void* ModuleResourceContainer::GetData(int index) const +{ + return mz_zip_reader_extract_to_heap(&d->m_ZipArchive, index, NULL, 0); +} + +const ModuleInfo*ModuleResourceContainer::GetModuleInfo() const +{ + return d->m_ModuleInfo; +} + +void ModuleResourceContainer::GetChildren(const std::string& resourcePath, bool relativePaths, + std::vector& names, std::vector& indices) const +{ + d->InitSortedEntries(); + + ModuleResourceContainerPrivate::SetType::const_iterator iter = + d->m_SortedEntries.find(std::make_pair(resourcePath, 0)); + if (iter == d->m_SortedEntries.end()) + { + return; + } + + for (++iter; iter != d->m_SortedEntries.end(); ++iter) + { + if (resourcePath.size() > iter->first.size()) break; + if (iter->first.compare(0, resourcePath.size(), resourcePath) == 0) + { + std::size_t pos = iter->first.find_first_of('/', resourcePath.size()); + if (pos == std::string::npos || pos == iter->first.size()-1) + { + if (relativePaths) + { + names.push_back(iter->first.substr(resourcePath.size())); + } + else + { + names.push_back(iter->first); + } + indices.push_back(iter->second); + } + } + } +} + +void ModuleResourceContainer::FindNodes(const std::string& path, const std::string& filePattern, + bool recurse, std::vector& resources) const +{ + std::vector names; + std::vector indices; + + this->GetChildren(path, true, names, indices); + + for(std::size_t i = 0, s = names.size(); i < s; ++i) + { + if (*names[i].rbegin() == '/' && recurse) + { + this->FindNodes(path + names[i], filePattern, recurse, resources); + } + if (this->Matches(names[i], filePattern)) + { + resources.push_back(ModuleResource(indices[i], *this)); + } + } +} + +bool ModuleResourceContainer::Matches(const std::string& name, const std::string& filePattern) const +{ + // short-cut + if (filePattern == "*") return true; + + std::stringstream ss(filePattern); + std::string tok; + std::size_t pos = 0; + while(std::getline(ss, tok, '*')) + { + std::size_t index = name.find(tok, pos); + if (index == std::string::npos) return false; + pos = index + tok.size(); + } + return true; +} + +US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleResourceContainer_p.h b/Core/CppMicroServices/core/src/module/usModuleResourceContainer_p.h new file mode 100644 index 0000000000..31827c52f4 --- /dev/null +++ b/Core/CppMicroServices/core/src/module/usModuleResourceContainer_p.h @@ -0,0 +1,88 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#ifndef USMODULERESOURCECONTAINER_P_H +#define USMODULERESOURCECONTAINER_P_H + +#include "usGlobalConfig.h" +#include "us_stdint.h" + +#include +#include +#include + +US_BEGIN_NAMESPACE + +struct ModuleInfo; +class ModuleResource; +struct ModuleResourceContainerPrivate; + +class ModuleResourceContainer +{ + +public: + + ModuleResourceContainer(const ModuleInfo* moduleInfo); + ~ModuleResourceContainer(); + + struct Stat + { + Stat() + : index(-1) + , uncompressedSize(0) + , modifiedTime(0) + , isDir(false) + {} + + std::string filePath; + int index; + int uncompressedSize; + time_t modifiedTime; + bool isDir; + }; + + bool IsValid() const; + + bool GetStat(Stat& stat) const; + bool GetStat(int index, Stat& stat) const; + + void* GetData(int index) const; + + const ModuleInfo* GetModuleInfo() const; + + void GetChildren(const std::string& resourcePath, bool relativePaths, + std::vector& names, std::vector& indices) const; + + void FindNodes(const std::string& path, const std::string& filePattern, + bool recurse, std::vector& resources) const; + +private: + + bool Matches(const std::string& name, const std::string& filePattern) const; + + ModuleResourceContainerPrivate* d; + +}; + + +US_END_NAMESPACE + +#endif // USMODULERESOURCECONTAINER_P_H diff --git a/Core/CppMicroServices/core/src/module/usModuleResourceStream.cpp b/Core/CppMicroServices/core/src/module/usModuleResourceStream.cpp index fd6fa8a448..b9970b15f2 100644 --- a/Core/CppMicroServices/core/src/module/usModuleResourceStream.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleResourceStream.cpp @@ -1,35 +1,39 @@ -/*=================================================================== +/*============================================================================= -BlueBerry Platform + Library: CppMicroServices -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -See LICENSE.txt or http://www.mitk.org for details. + http://www.apache.org/licenses/LICENSE-2.0 -===================================================================*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ #include "usModuleResourceStream.h" #include "usModuleResource.h" // 'this' used in base member initializer list US_MSVC_PUSH_DISABLE_WARNING(4355) US_BEGIN_NAMESPACE ModuleResourceStream::ModuleResourceStream(const ModuleResource& resource, std::ios_base::openmode mode) - : ModuleResourceBuffer(resource.GetData(), resource.GetSize(), mode | std::ios_base::in, - resource.IsCompressed()) + : ModuleResourceBuffer(resource.GetData(), resource.GetSize(), mode | std::ios_base::in) , std::istream(this) { } US_END_NAMESPACE US_MSVC_POP_WARNING diff --git a/Core/CppMicroServices/core/src/module/usModuleResourceTree.cpp b/Core/CppMicroServices/core/src/module/usModuleResourceTree.cpp deleted file mode 100644 index 6aa4351da5..0000000000 --- a/Core/CppMicroServices/core/src/module/usModuleResourceTree.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - - -#include "usModuleResourceTree_p.h" - -#include "usUtils_p.h" -#include "us_stdint.h" - -#include -#include - -//#define DEBUG_RESOURCE_MATCH - -US_BEGIN_NAMESPACE - -US_Core_EXPORT bool RegisterResourceData(int, ModuleInfo* moduleInfo, - ModuleInfo::ModuleResourceData resourceTree, - ModuleInfo::ModuleResourceData resourceNames, - ModuleInfo::ModuleResourceData resourceData) -{ - moduleInfo->resourceTree.push_back(resourceTree); - moduleInfo->resourceNames.push_back(resourceNames); - moduleInfo->resourceData.push_back(resourceData); - return true; -} - - -ModuleResourceTree::ModuleResourceTree(ModuleInfo::ModuleResourceData resourceTree, - ModuleInfo::ModuleResourceData resourceNames, - ModuleInfo::ModuleResourceData resourceData) - : isValid(resourceTree && resourceNames && resourceData) - , tree(resourceTree) - , names(resourceNames) - , payloads(resourceData) -{ -} - -bool ModuleResourceTree::IsValid() const -{ - return isValid; -} - -void ModuleResourceTree::Invalidate() -{ - isValid = false; -} - -inline std::string ModuleResourceTree::GetName(int node) const -{ - if(!node) // root - return std::string(); - - const int offset = FindOffset(node); - - std::string ret; - int nameOffset = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - const int16_t nameLength = (names[nameOffset+0] << 8) + - (names[nameOffset+1] << 0); - nameOffset += 2; // jump past name length - nameOffset += 4; // jump past hash - - ret.resize(nameLength); - for(int i = 0; i < nameLength; ++i) - { - ret[i] = names[nameOffset+i]; - } - return ret; -} - -int ModuleResourceTree::FindNode(const std::string& _path) const -{ - std::string path = _path.empty() ? "/" : _path; - -#ifdef DEBUG_RESOURCE_MATCH - US_DEBUG << "***" << " START " << path; -#endif - - if(path == "/") - return 0; - - // the root node is always first - int childCount = (tree[6] << 24) + (tree[7] << 16) + - (tree[8] << 8) + (tree[9] << 0); - int child = (tree[10] << 24) + (tree[11] << 16) + - (tree[12] << 8) + (tree[13] << 0); - - int node = -1; - - // split the full path into segments - std::vector segments; - { - std::stringstream ss(path); - std::string item; - while(std::getline(ss, item, '/')) - { - if (item.empty()) continue; - segments.push_back(item); - } - } - - //now iterate up the path hierarchy - for (std::size_t i = 0; i < segments.size() && childCount; ++i) - { - const std::string& segment = segments[i]; - -#ifdef DEBUG_RESOURCE_MATCH - US_DEBUG << " CHILDREN " << segment; - for(int j = 0; j < childCount; ++j) - { - US_DEBUG << " " << child+j << " :: " << GetName(child+j); - } -#endif - - // get the hash value for the current segment - const uint32_t currHash = static_cast(US_HASH_FUNCTION_NAMESPACE::US_HASH_FUNCTION(std::string,segment)); - - // do a binary search for the hash - int l = 0; - int r = childCount-1; - int subNode = (l+r+1)/2; - while(r != l) - { - const uint32_t subNodeHash = GetHash(child+subNode); - if(currHash == subNodeHash) - break; - else if(currHash < subNodeHash) - r = subNode - 1; - else - l = subNode; - subNode = (l+r+1) / 2; - } - subNode += child; - - // now check fo collisions and do compare using equality - bool found = false; - if(GetHash(subNode) == currHash) - { - while(subNode > child && GetHash(subNode-1) == currHash) // walk up to include all collisions - --subNode; - for(; subNode < child+childCount && GetHash(subNode) == currHash; ++subNode) - { - // now test using name comparison - if(GetName(subNode) == segment) - { - found = true; - int offset = FindOffset(subNode); -#ifdef DEBUG_RESOURCE_MATCH - US_DEBUG << " TRY " << subNode << " " << GetName(subNode) << " " << offset; -#endif - offset += 4; // jump past name - - const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0); - offset += 2; - - if(i == segments.size()-1) - { -#ifdef DEBUG_RESOURCE_MATCH - US_DEBUG << "!!!!" << " FINISHED " << subNode; -#endif - return subNode; - } - - // if we are not at the end of the resource path and the current - // segment is not a directory, return "not found" (this shouldn't happen). - if(!(flags & Directory)) - return -1; - - childCount = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - offset += 4; - child = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - break; - } - } - } - if(!found) - { - break; - } - } -#ifdef DEBUG_RESOURCE_MATCH - US_DEBUG << "***" << " FINISHED " << node; -#endif - return node; -} - -void ModuleResourceTree::FindNodes(const std::string& _path, - const std::string& _filePattern, - bool recurse, std::vector& result) -{ - std::string path = _path.empty() ? std::string("/") : _path; - if (path[path.size()-1] != '/') path.append("/"); - - std::string filePattern = _filePattern.empty() ? std::string("*") : _filePattern; - - int rootNode = FindNode(path); - if (rootNode > -1) - { - this->FindNodes(result, path, rootNode, filePattern, recurse); - } -} - -short ModuleResourceTree::GetFlags(int node) const -{ - if(node == -1) - return 0; - const int offset = FindOffset(node) + 4; //jump past name - return (tree[offset+0] << 8) + (tree[offset+1] << 0); -} - -void ModuleResourceTree::FindNodes(std::vector &result, const std::string& path, - const int rootNode, - const std::string &filePattern, bool recurse) -{ - int offset = FindOffset(rootNode) + 6; // jump past name and type - - const int childCount = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - offset += 4; - const int childNode = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - - for(int i = childNode; i < childNode+childCount; ++i) - { - const int childOffset = FindOffset(i); - const short childFlags = (tree[childOffset+4] << 8) + (tree[childOffset+5] << 0); - if (!(childFlags & Directory)) - { - const std::string name = GetName(i); - if (this->Matches(name, filePattern)) - { - result.push_back(path + name); - } - } - else if (recurse) - { - this->FindNodes(result, path + GetName(i) + "/", i, filePattern, recurse); - } - } -} - -uint32_t ModuleResourceTree::GetHash(int node) const -{ - if(!node) // root node - return 0; - - const int offset = FindOffset(node); - int nameOffset = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - nameOffset += 2; // jump past name length - return (names[nameOffset+0] << 24) + (names[nameOffset+1] << 16) + - (names[nameOffset+2] << 8) + (names[nameOffset+3] << 0); -} - -bool ModuleResourceTree::Matches(const std::string &name, const std::string &filePattern) -{ - // short-cut - if (filePattern == "*") return true; - - std::stringstream ss(filePattern); - std::string tok; - std::size_t pos = 0; - while(std::getline(ss, tok, '*')) - { - std::size_t index = name.find(tok, pos); - if (index == std::string::npos) return false; - pos = index + tok.size(); - } - return true; -} - -const unsigned char* ModuleResourceTree::GetData(int node, int32_t* size) const -{ - if(node == -1) - { - *size = 0; - return 0; - } - int offset = FindOffset(node) + 4; // jump past name - - const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0); - offset += 2; - - offset += 4; // jump past the padding - - if(!(flags & Directory)) - { - const int dataOffset = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - const int32_t dataLength = (payloads[dataOffset+0] << 24) + (payloads[dataOffset+1] << 16) + - (payloads[dataOffset+2] << 8) + (payloads[dataOffset+3] << 0); - const unsigned char* ret = payloads+dataOffset+4; - *size = dataLength; - return ret; - } - *size = 0; - return 0; -} - -void ModuleResourceTree::GetChildren(int node, std::vector& ret) const -{ - if(node == -1) - return; - - int offset = FindOffset(node) + 4; // jump past name - - const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0); - offset += 2; - - if(flags & Directory) - { - const int childCount = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - offset += 4; - const int childOffset = (tree[offset+0] << 24) + (tree[offset+1] << 16) + - (tree[offset+2] << 8) + (tree[offset+3] << 0); - for(int i = childOffset; i < childOffset+childCount; ++i) - { - ret.push_back(GetName(i)); - } - } -} - -US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleResourceTree_p.h b/Core/CppMicroServices/core/src/module/usModuleResourceTree_p.h deleted file mode 100644 index 6584ae4203..0000000000 --- a/Core/CppMicroServices/core/src/module/usModuleResourceTree_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - - -#ifndef USMODULERESOURCETREE_H -#define USMODULERESOURCETREE_H - -#include "usModuleInfo.h" -#include "us_stdint.h" - -#include - -US_BEGIN_NAMESPACE - -struct ModuleInfo; - -/* - * This class represents the triple of data pointers - * (us_resource_data, us_resource_name, us_resource_tree) generate by - * the resource compiler. - * - * Each module owns zero or one such triple but in case of a statically - * linked (imported) module, the resource trees are "merged" together. - */ -class ModuleResourceTree -{ - -private: - - enum Flags - { - Directory = 0x01, - Compressed = 0x02 - }; - - bool isValid; - const unsigned char *tree, *names, *payloads; - - // Returns the offset in the us_resource_tree array for a given node index - inline int FindOffset(int node) const { return node * 14; } // sizeof each tree element - - std::string GetName(int node) const; - short GetFlags(int node) const; - - void FindNodes(std::vector& result, const std::string& path, - const int rootNode, - const std::string& filePattern, bool recurse); - - uint32_t GetHash(int node) const; - - bool Matches(const std::string& name, const std::string& filePattern); - -public: - - ModuleResourceTree(ModuleInfo::ModuleResourceData resourceTree, - ModuleInfo::ModuleResourceData resourceNames, - ModuleInfo::ModuleResourceData resourceData); - - bool IsValid() const; - void Invalidate(); - - // Returns an index enumerating the info entries in the us_resource_tree array for - // the given resource path. - int FindNode(const std::string& path) const; - - void FindNodes(const std::string& path, const std::string& filePattern, - bool recurse, std::vector& result); - - inline bool IsCompressed(int node) const {return GetFlags(node) & Compressed ? true : false; } - inline bool IsDir(int node) const { return GetFlags(node) & Directory ? true : false; } - const unsigned char* GetData(int node, int32_t *size) const; - void GetChildren(int node, std::vector& children) const; - -}; - -US_END_NAMESPACE - -#endif // USMODULERESOURCETREE_H diff --git a/Core/CppMicroServices/core/src/module/usModuleSettings.cpp b/Core/CppMicroServices/core/src/module/usModuleSettings.cpp index 9249e18273..98271a8693 100644 --- a/Core/CppMicroServices/core/src/module/usModuleSettings.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleSettings.cpp @@ -1,175 +1,189 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleSettings.h" #include "usThreads_p.h" #include "usStaticInit_p.h" #include #include #include #include #include US_BEGIN_NAMESPACE namespace { std::string RemoveTrailingPathSeparator(const std::string& in) { #ifdef US_PLATFORM_WINDOWS const char separator = '\\'; #else const char separator = '/'; #endif if (in.empty()) return in; std::string::const_iterator lastChar = --in.end(); while (lastChar != in.begin() && std::isspace(*lastChar)) lastChar--; if (*lastChar != separator) lastChar++; std::string::const_iterator firstChar = in.begin(); while (firstChar < lastChar && std::isspace(*firstChar)) firstChar++; return std::string(firstChar, lastChar); } } std::string ModuleSettings::CURRENT_MODULE_PATH() { static const std::string var = "us_current_module_path"; return var; } struct ModuleSettingsPrivate : public MultiThreaded<> { ModuleSettingsPrivate() : autoLoadPaths() #ifdef US_ENABLE_AUTOLOADING_SUPPORT , autoLoadingEnabled(true) #else , autoLoadingEnabled(false) #endif , autoLoadingDisabled(false) + , logLevel(DebugMsg) { autoLoadPaths.insert(ModuleSettings::CURRENT_MODULE_PATH()); char* envPaths = getenv("US_AUTOLOAD_PATHS"); if (envPaths != NULL) { std::stringstream ss(envPaths); std::string envPath; #ifdef US_PLATFORM_WINDOWS const char separator = ';'; #else const char separator = ':'; #endif while (std::getline(ss, envPath, separator)) { std::string normalizedEnvPath = RemoveTrailingPathSeparator(envPath); if (!normalizedEnvPath.empty()) { extraPaths.insert(normalizedEnvPath); } } } if (getenv("US_DISABLE_AUTOLOADING")) { autoLoadingDisabled = true; } } std::set autoLoadPaths; std::set extraPaths; bool autoLoadingEnabled; bool autoLoadingDisabled; std::string storagePath; + MsgType logLevel; }; US_GLOBAL_STATIC(ModuleSettingsPrivate, moduleSettingsPrivate) bool ModuleSettings::IsThreadingSupportEnabled() { #ifdef US_ENABLE_THREADING_SUPPORT return true; #else return false; #endif } bool ModuleSettings::IsAutoLoadingEnabled() { US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); #ifdef US_ENABLE_AUTOLOADING_SUPPORT return !moduleSettingsPrivate()->autoLoadingDisabled && moduleSettingsPrivate()->autoLoadingEnabled; #else return false; #endif } void ModuleSettings::SetAutoLoadingEnabled(bool enable) { US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); moduleSettingsPrivate()->autoLoadingEnabled = enable; } ModuleSettings::PathList ModuleSettings::GetAutoLoadPaths() { US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); ModuleSettings::PathList paths(moduleSettingsPrivate()->autoLoadPaths.begin(), moduleSettingsPrivate()->autoLoadPaths.end()); paths.insert(paths.end(), moduleSettingsPrivate()->extraPaths.begin(), moduleSettingsPrivate()->extraPaths.end()); std::sort(paths.begin(), paths.end()); paths.erase(std::unique(paths.begin(), paths.end()), paths.end()); return paths; } void ModuleSettings::SetAutoLoadPaths(const PathList& paths) { PathList normalizedPaths; normalizedPaths.resize(paths.size()); std::transform(paths.begin(), paths.end(), normalizedPaths.begin(), RemoveTrailingPathSeparator); US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); moduleSettingsPrivate()->autoLoadPaths.clear(); moduleSettingsPrivate()->autoLoadPaths.insert(normalizedPaths.begin(), normalizedPaths.end()); } void ModuleSettings::AddAutoLoadPath(const std::string& path) { US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); moduleSettingsPrivate()->autoLoadPaths.insert(RemoveTrailingPathSeparator(path)); } void ModuleSettings::SetStoragePath(const std::string &path) { US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); moduleSettingsPrivate()->storagePath = RemoveTrailingPathSeparator(path); } std::string ModuleSettings::GetStoragePath() { US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); return moduleSettingsPrivate()->storagePath; } +void ModuleSettings::SetLogLevel(MsgType level) +{ + US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); + moduleSettingsPrivate()->logLevel = level; +} + +MsgType ModuleSettings::GetLogLevel() +{ + US_UNUSED(ModuleSettingsPrivate::Lock(moduleSettingsPrivate())); + return moduleSettingsPrivate()->logLevel; +} + US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleUtils.cpp b/Core/CppMicroServices/core/src/module/usModuleUtils.cpp index aa7c7c8db9..e38e3a5a65 100644 --- a/Core/CppMicroServices/core/src/module/usModuleUtils.cpp +++ b/Core/CppMicroServices/core/src/module/usModuleUtils.cpp @@ -1,176 +1,171 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleUtils_p.h" + #include +#include #include US_BEGIN_NAMESPACE namespace { #ifdef US_BUILD_SHARED_LIBS const bool sharedLibMode = true; #else const bool sharedLibMode = false; #endif } #ifdef __GNUC__ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include -std::string GetLibraryPath_impl(const std::string& /*libName*/, void* symbol) +std::string GetLibraryPath_impl(void* symbol) { Dl_info info; if (dladdr(symbol, &info)) { return info.dli_fname; } else { - US_DEBUG << "GetLibraryPath_impl() dladdr() failed: " << dlerror(); + US_DEBUG << "GetLibraryPath_impl() failed for address " << symbol; } return ""; } -void* GetSymbol_impl(const std::string& libName, const char* symbol) +void* GetSymbol_impl(const ModuleInfo& moduleInfo, const char* symbol) { // Clear the last error message dlerror(); void* selfHandle = 0; - if (libName.empty() || !sharedLibMode) + if (!sharedLibMode || moduleInfo.name == "main") { // Get the handle of the executable selfHandle = dlopen(0, RTLD_LAZY); } else { - selfHandle = dlopen(libName.c_str(), RTLD_LAZY); + selfHandle = dlopen(moduleInfo.location.c_str(), RTLD_LAZY); } if (selfHandle) { void* addr = dlsym(selfHandle, symbol); if (!addr) { const char* dlerrorMsg = dlerror(); if (dlerrorMsg) { US_DEBUG << "GetSymbol_impl() failed: " << dlerrorMsg; } } dlclose(selfHandle); return addr; } else { US_DEBUG << "GetSymbol_impl() dlopen() failed: " << dlerror(); } return 0; } #elif _WIN32 #include -std::string GetLibraryPath_impl(const std::string& libName, void *symbol) +std::string GetLibraryPath_impl(void *symbol) { HMODULE handle = 0; - if (libName.empty() || !sharedLibMode) - { - // get the handle for the executable - handle = GetModuleHandle(NULL); - } - else - { - handle = GetModuleHandle(libName.c_str()); - } - if (!handle) + BOOL handleError = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + static_cast(symbol), &handle); + if (!handleError) { // Test US_DEBUG << "GetLibraryPath_impl():GetModuleHandle() " << GetLastErrorStr(); return ""; } char modulePath[512]; if (GetModuleFileName(handle, modulePath, 512)) { return modulePath; } US_DEBUG << "GetLibraryPath_impl():GetModuleFileName() " << GetLastErrorStr(); return ""; } -void* GetSymbol_impl(const std::string& libName, const char* symbol) +void* GetSymbol_impl(const ModuleInfo& moduleInfo, const char* symbol) { HMODULE handle = NULL; - if (libName.empty() || !sharedLibMode) + if (!sharedLibMode || moduleInfo.name == "main") { handle = GetModuleHandle(NULL); } else { - handle = GetModuleHandle(libName.c_str()); + handle = GetModuleHandle(moduleInfo.location.c_str()); } if (!handle) { US_DEBUG << "GetSymbol_impl():GetModuleHandle() " << GetLastErrorStr(); return 0; } void* addr = (void*)GetProcAddress(handle, symbol); if (!addr) { US_DEBUG << "GetSymbol_impl():GetProcAddress(handle," << symbol << ") " << GetLastErrorStr(); } return addr; } #else -std::string GetLibraryPath_impl(const std::string& libName, void* symbol) +std::string GetLibraryPath_impl(void*) { return ""; } -void* GetSymbol_impl(const std::string& libName, const char* symbol) +void* GetSymbol_impl(const ModuleInfo&, const char* symbol) { return 0; } #endif -std::string ModuleUtils::GetLibraryPath(const std::string& libName, void* symbol) +std::string ModuleUtils::GetLibraryPath(void* symbol) { - return GetLibraryPath_impl(libName, symbol); + return GetLibraryPath_impl(symbol); } -void* ModuleUtils::GetSymbol(const std::string& libName, const char* symbol) +void* ModuleUtils::GetSymbol(const ModuleInfo& module, const char* symbol) { - return GetSymbol_impl(libName, symbol); + return GetSymbol_impl(module, symbol); } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/module/usModuleUtils_p.h b/Core/CppMicroServices/core/src/module/usModuleUtils_p.h index 4a55394e42..efd7d86b29 100644 --- a/Core/CppMicroServices/core/src/module/usModuleUtils_p.h +++ b/Core/CppMicroServices/core/src/module/usModuleUtils_p.h @@ -1,45 +1,52 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEUTILS_H #define USMODULEUTILS_H #include #include +#define US_STR_(x) #x +#define US_STR(x) US_STR_(x) +#define US_CONCAT_(x,y) x ## y +#define US_CONCAT(x,y) US_CONCAT_(x,y) + US_BEGIN_NAMESPACE +struct ModuleInfo; + /** * This class is not intended to be used directly. It is exported to support * the CppMicroServices module system. */ struct US_Core_EXPORT ModuleUtils { - static std::string GetLibraryPath(const std::string& libName, void* symbol); + static std::string GetLibraryPath(void* symbol); - static void* GetSymbol(const std::string& libName, const char* symbol); + static void* GetSymbol(const ModuleInfo& module, const char* symbol); }; US_END_NAMESPACE #endif // USMODULEUTILS_H diff --git a/Core/CppMicroServices/core/src/service/usLDAPExpr.cpp b/Core/CppMicroServices/core/src/service/usLDAPExpr.cpp index 252d2c8b7c..b31f1bf9b8 100644 --- a/Core/CppMicroServices/core/src/service/usLDAPExpr.cpp +++ b/Core/CppMicroServices/core/src/service/usLDAPExpr.cpp @@ -1,823 +1,851 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usLDAPExpr_p.h" #include "usAny.h" #include "usServicePropertiesImpl_p.h" #include #include #include #include #include #include #include US_BEGIN_NAMESPACE -const int LDAPExpr::AND = 0; -const int LDAPExpr::OR = 1; -const int LDAPExpr::NOT = 2; -const int LDAPExpr::EQ = 4; -const int LDAPExpr::LE = 8; -const int LDAPExpr::GE = 16; -const int LDAPExpr::APPROX = 32; -const int LDAPExpr::COMPLEX = LDAPExpr::AND | LDAPExpr::OR | LDAPExpr::NOT; -const int LDAPExpr::SIMPLE = LDAPExpr::EQ | LDAPExpr::LE | LDAPExpr::GE | LDAPExpr::APPROX; - -const LDAPExpr::Byte LDAPExpr::WILDCARD = std::numeric_limits::max(); -const std::string LDAPExpr::WILDCARD_STRING = std::string(1, LDAPExpr::WILDCARD ); -const std::string LDAPExpr::NULLQ = "Null query"; -const std::string LDAPExpr::GARBAGE = "Trailing garbage"; -const std::string LDAPExpr::EOS = "Unexpected end of query"; -const std::string LDAPExpr::MALFORMED = "Malformed query"; -const std::string LDAPExpr::OPERATOR = "Undefined operator"; +namespace LDAPExprConstants { + +static LDAPExpr::Byte WILDCARD() +{ + static LDAPExpr::Byte b = std::numeric_limits::max(); + return b; +} + +static const std::string& WILDCARD_STRING() +{ + static std::string s(1, WILDCARD()); + return s; +} + +static const std::string& NULLQ() +{ + static std::string s = "Null query"; + return s; +} + +static const std::string& GARBAGE() +{ + static std::string s = "Trailing garbage"; + return s; +} + +static const std::string& EOS() +{ + static std::string s = "Unexpected end of query"; + return s; +} + +static const std::string& MALFORMED() +{ + static std::string s = "Malformed query"; + return s; +} + +static const std::string& OPERATOR() +{ + static std::string s = "Undefined operator"; + return s; +} + +} bool stricomp(const std::string::value_type& v1, const std::string::value_type& v2) { return ::tolower(v1) == ::tolower(v2); } //! Contains the current parser position and parsing utility methods. class LDAPExpr::ParseState { private: std::size_t m_pos; std::string m_str; public: ParseState(const std::string &str); //! Move m_pos to remove the prefix \a pre bool prefix(const std::string &pre); /** Peek a char at m_pos \note If index out of bounds, throw exception */ LDAPExpr::Byte peek(); //! Increment m_pos by n void skip(int n); //! return string from m_pos until the end std::string rest() const; //! Move m_pos until there's no spaces void skipWhite(); //! Get string until special chars. Move m_pos std::string getAttributeName(); //! Get string and convert * to WILDCARD std::string getAttributeValue(); //! Throw InvalidSyntaxException exception void error(const std::string &m) const; }; class LDAPExprData : public SharedData { public: LDAPExprData( int op, const std::vector& args ) : m_operator(op), m_args(args), m_attrName(), m_attrValue() { } LDAPExprData( int op, std::string attrName, const std::string& attrValue ) : m_operator(op), m_args(), m_attrName(attrName), m_attrValue(attrValue) { } LDAPExprData( const LDAPExprData& other ) : SharedData(other), m_operator(other.m_operator), m_args(other.m_args), m_attrName(other.m_attrName), m_attrValue(other.m_attrValue) { } int m_operator; std::vector m_args; std::string m_attrName; std::string m_attrValue; }; LDAPExpr::LDAPExpr() : d() { } LDAPExpr::LDAPExpr( const std::string &filter ) : d() { ParseState ps(filter); try { LDAPExpr expr = ParseExpr(ps); if (!Trim(ps.rest()).empty()) { - ps.error(GARBAGE + " '" + ps.rest() + "'"); + ps.error(LDAPExprConstants::GARBAGE() + " '" + ps.rest() + "'"); } d = expr.d; } catch (const std::out_of_range&) { - ps.error(EOS); + ps.error(LDAPExprConstants::EOS()); } } LDAPExpr::LDAPExpr( int op, const std::vector& args ) : d(new LDAPExprData(op, args)) { } LDAPExpr::LDAPExpr( int op, const std::string &attrName, const std::string &attrValue ) : d(new LDAPExprData(op, attrName, attrValue)) { } LDAPExpr::LDAPExpr( const LDAPExpr& other ) : d(other.d) { } LDAPExpr& LDAPExpr::operator=(const LDAPExpr& other) { d = other.d; return *this; } LDAPExpr::~LDAPExpr() { } std::string LDAPExpr::Trim(std::string str) { str.erase(0, str.find_first_not_of(' ')); str.erase(str.find_last_not_of(' ')+1); return str; } bool LDAPExpr::GetMatchedObjectClasses(ObjectClassSet& objClasses) const { if (d->m_operator == EQ) { if (d->m_attrName.length() == ServiceConstants::OBJECTCLASS().length() && std::equal(d->m_attrName.begin(), d->m_attrName.end(), ServiceConstants::OBJECTCLASS().begin(), stricomp) && - d->m_attrValue.find(WILDCARD) == std::string::npos) + d->m_attrValue.find(LDAPExprConstants::WILDCARD()) == std::string::npos) { objClasses.insert( d->m_attrValue ); return true; } return false; } else if (d->m_operator == AND) { bool result = false; for (std::size_t i = 0; i < d->m_args.size( ); i++) { LDAPExpr::ObjectClassSet r; if (d->m_args[i].GetMatchedObjectClasses(r)) { result = true; if (objClasses.empty()) { objClasses = r; } else { // if AND op and classes in several operands, // then only the intersection is possible. LDAPExpr::ObjectClassSet::iterator it1 = objClasses.begin(); LDAPExpr::ObjectClassSet::iterator it2 = r.begin(); while ( (it1 != objClasses.end()) && (it2 != r.end()) ) { if (*it1 < *it2) { objClasses.erase(it1++); } else if (*it2 < *it1) { ++it2; } else { // *it1 == *it2 ++it1; ++it2; } } // Anything left in set_1 from here on did not appear in set_2, // so we remove it. objClasses.erase(it1, objClasses.end()); } } } return result; } else if (d->m_operator == OR) { for (std::size_t i = 0; i < d->m_args.size( ); i++) { LDAPExpr::ObjectClassSet r; if (d->m_args[i].GetMatchedObjectClasses(r)) { std::copy(r.begin(), r.end(), std::inserter(objClasses, objClasses.begin())); } else { objClasses.clear(); return false; } } return true; } return false; } std::string LDAPExpr::ToLower(const std::string& str) { std::string lowerStr(str); std::transform(str.begin(), str.end(), lowerStr.begin(), ::tolower); return lowerStr; } bool LDAPExpr::IsSimple(const StringList& keywords, LocalCache& cache, bool matchCase ) const { if (cache.empty()) { cache.resize(keywords.size()); } if (d->m_operator == EQ) { StringList::const_iterator index; if ((index = std::find(keywords.begin(), keywords.end(), matchCase ? d->m_attrName : ToLower(d->m_attrName))) != keywords.end() && - d->m_attrValue.find_first_of(WILDCARD) == std::string::npos) + d->m_attrValue.find_first_of(LDAPExprConstants::WILDCARD()) == std::string::npos) { cache[index - keywords.begin()] = StringList(1, d->m_attrValue); return true; } } else if (d->m_operator == OR) { for (std::size_t i = 0; i < d->m_args.size( ); i++) { if (!d->m_args[i].IsSimple(keywords, cache, matchCase)) return false; } return true; } return false; } bool LDAPExpr::IsNull() const { return !d; } bool LDAPExpr::Query( const std::string& filter, const ServicePropertiesImpl& pd) { return LDAPExpr(filter).Evaluate(pd, false); } bool LDAPExpr::Evaluate( const ServicePropertiesImpl& p, bool matchCase ) const { if ((d->m_operator & SIMPLE) != 0) { // try case sensitive match first int index = p.FindCaseSensitive(d->m_attrName); if (index < 0 && !matchCase) index = p.Find(d->m_attrName); return index < 0 ? false : Compare(p.Value(index), d->m_operator, d->m_attrValue); } else { // (d->m_operator & COMPLEX) != 0 switch (d->m_operator) { case AND: for (std::size_t i = 0; i < d->m_args.size(); i++) { if (!d->m_args[i].Evaluate(p, matchCase)) return false; } return true; case OR: for (std::size_t i = 0; i < d->m_args.size(); i++) { if (d->m_args[i].Evaluate(p, matchCase)) return true; } return false; case NOT: return !d->m_args[0].Evaluate(p, matchCase); default: return false; // Cannot happen } } } bool LDAPExpr::Compare( const Any& obj, int op, const std::string& s ) const { if (obj.Empty()) return false; - if (op == EQ && s == WILDCARD_STRING) + if (op == EQ && s == LDAPExprConstants::WILDCARD_STRING()) return true; try { const std::type_info& objType = obj.Type(); if (objType == typeid(std::string)) { return CompareString(ref_any_cast(obj), op, s); } else if (objType == typeid(std::vector)) { const std::vector& list = ref_any_cast >(obj); for (std::size_t it = 0; it != list.size(); it++) { if (CompareString(list[it], op, s)) return true; } } else if (objType == typeid(std::list)) { const std::list& list = ref_any_cast >(obj); for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) { if (CompareString(*it, op, s)) return true; } } else if (objType == typeid(char)) { return CompareString(std::string(1, ref_any_cast(obj)), op, s); } else if (objType == typeid(bool)) { if (op==LE || op==GE) return false; std::string boolVal = any_cast(obj) ? "true" : "false"; return std::equal(s.begin(), s.end(), boolVal.begin(), stricomp); } else if (objType == typeid(short)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(long long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned char)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned short)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned long long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(float)) { errno = 0; char* endptr = 0; double sFloat = strtod(s.c_str(), &endptr); if ((errno == ERANGE && (sFloat == 0 || sFloat == HUGE_VAL || sFloat == -HUGE_VAL)) || (errno != 0 && sFloat == 0) || endptr == s.c_str()) { return false; } double floatVal = static_cast(any_cast(obj)); switch(op) { case LE: return floatVal <= sFloat; case GE: return floatVal >= sFloat; default: /*APPROX and EQ*/ double diff = floatVal - sFloat; return (diff < std::numeric_limits::epsilon()) && (diff > -std::numeric_limits::epsilon()); } } else if (objType == typeid(double)) { errno = 0; char* endptr = 0; double sDouble = strtod(s.c_str(), &endptr); if ((errno == ERANGE && (sDouble == 0 || sDouble == HUGE_VAL || sDouble == -HUGE_VAL)) || (errno != 0 && sDouble == 0) || endptr == s.c_str()) { return false; } double doubleVal = any_cast(obj); switch(op) { case LE: return doubleVal <= sDouble; case GE: return doubleVal >= sDouble; default: /*APPROX and EQ*/ double diff = doubleVal - sDouble; return (diff < std::numeric_limits::epsilon()) && (diff > -std::numeric_limits::epsilon()); } } else if (objType == typeid(std::vector)) { const std::vector& list = ref_any_cast >(obj); for (std::size_t it = 0; it != list.size(); it++) { if (Compare(list[it], op, s)) return true; } } } catch (...) { // This might happen if a std::string-to-datatype conversion fails // Just consider it a false match and ignore the exception } return false; } template bool LDAPExpr::CompareIntegralType(const Any& obj, const int op, const std::string& s) const { errno = 0; char* endptr = 0; long longInt = strtol(s.c_str(), &endptr, 10); if ((errno == ERANGE && (longInt == std::numeric_limits::max() || longInt == std::numeric_limits::min())) || (errno != 0 && longInt == 0) || endptr == s.c_str()) { return false; } T sInt = static_cast(longInt); T intVal = any_cast(obj); switch(op) { case LE: return intVal <= sInt; case GE: return intVal >= sInt; default: /*APPROX and EQ*/ return intVal == sInt; } } bool LDAPExpr::CompareString( const std::string& s1, int op, const std::string& s2 ) { switch(op) { case LE: return s1.compare(s2) <= 0; case GE: return s1.compare(s2) >= 0; case EQ: return PatSubstr(s1,s2); case APPROX: return FixupString(s2) == FixupString(s1); default: return false; } } std::string LDAPExpr::FixupString( const std::string& s ) { std::string sb; sb.reserve(s.size()); std::size_t len = s.length(); for(std::size_t i=0; im_operator std::vector v; do { v.push_back(ParseExpr(ps)); ps.skipWhite(); } while (ps.peek() == '('); std::size_t n = v.size(); if (!ps.prefix(")") || n == 0 || (op == NOT && n > 1)) - ps.error(MALFORMED); + ps.error(LDAPExprConstants::MALFORMED()); return LDAPExpr(op, v); } LDAPExpr LDAPExpr::ParseSimple( ParseState &ps ) { std::string attrName = ps.getAttributeName(); if (attrName.empty()) - ps.error(MALFORMED); + ps.error(LDAPExprConstants::MALFORMED()); int op = 0; if (ps.prefix("=")) op = EQ; else if (ps.prefix("<=")) op = LE; else if(ps.prefix(">=")) op = GE; else if(ps.prefix("~=")) op = APPROX; else { // System.out.println("undef op='" + ps.peek() + "'"); - ps.error(OPERATOR); // Does not return + ps.error(LDAPExprConstants::OPERATOR()); // Does not return } std::string attrValue = ps.getAttributeValue(); if (!ps.prefix(")")) - ps.error(MALFORMED); + ps.error(LDAPExprConstants::MALFORMED()); return LDAPExpr(op, attrName, attrValue); } const std::string LDAPExpr::ToString() const { std::string res; res.append("("); if ((d->m_operator & SIMPLE) != 0) { res.append(d->m_attrName); switch (d->m_operator) { case EQ: res.append("="); break; case LE: res.append("<="); break; case GE: res.append(">="); break; case APPROX: res.append("~="); break; } for (std::size_t i = 0; i < d->m_attrValue.length(); i++) { Byte c = d->m_attrValue.at(i); if (c == '(' || c == ')' || c == '*' || c == '\\') { res.append(1, '\\'); } - else if (c == WILDCARD) + else if (c == LDAPExprConstants::WILDCARD()) { c = '*'; } res.append(1, c); } } else { switch (d->m_operator) { case AND: res.append("&"); break; case OR: res.append("|"); break; case NOT: res.append("!"); break; } for (std::size_t i = 0; i < d->m_args.size(); i++) { res.append(d->m_args[i].ToString()); } } res.append(")"); return res; } LDAPExpr::ParseState::ParseState( const std::string& str ) : m_pos(0), m_str() { if (str.empty()) { - error(NULLQ); + error(LDAPExprConstants::NULLQ()); } m_str = str; } bool LDAPExpr::ParseState::prefix( const std::string& pre ) { std::string::iterator startIter = m_str.begin() + m_pos; if (!std::equal(pre.begin(), pre.end(), startIter)) return false; m_pos += pre.size(); return true; } char LDAPExpr::ParseState::peek() { if ( m_pos >= m_str.size() ) { throw std::out_of_range( "LDAPExpr" ); } return m_str.at(m_pos); } void LDAPExpr::ParseState::skip( int n ) { m_pos += n; } std::string LDAPExpr::ParseState::rest() const { return m_str.substr(m_pos); } void LDAPExpr::ParseState::skipWhite() { while (std::isspace(peek())) { m_pos++; } } std::string LDAPExpr::ParseState::getAttributeName() { std::size_t start = m_pos; std::size_t n = 0; bool nIsSet = false; for(;; m_pos++) { Byte c = peek(); if (c == '(' || c == ')' || c == '<' || c == '>' || c == '=' || c == '~') { break; } else if (!std::isspace(c)) { n = m_pos - start + 1; nIsSet = true; } } if (!nIsSet) { return std::string(); } return m_str.substr(start, n); } std::string LDAPExpr::ParseState::getAttributeValue() { std::string sb; bool exit = false; while( !exit ) { Byte c = peek( ); switch(c) { case '(': case ')': exit = true; break; case '*': - sb.append(1, WILDCARD); + sb.append(1, LDAPExprConstants::WILDCARD()); break; case '\\': sb.append(1, m_str.at(++m_pos)); break; default: sb.append(1, c); break; } if ( !exit ) { m_pos++; } } return sb; } void LDAPExpr::ParseState::error( const std::string &m ) const { std::string errorStr = m + ": " + (m_str.empty() ? "" : m_str.substr(m_pos)); throw std::invalid_argument(errorStr); } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/service/usLDAPExpr_p.h b/Core/CppMicroServices/core/src/service/usLDAPExpr_p.h index d1da7fac93..f9be03ffae 100644 --- a/Core/CppMicroServices/core/src/service/usLDAPExpr_p.h +++ b/Core/CppMicroServices/core/src/service/usLDAPExpr_p.h @@ -1,188 +1,178 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USLDAPEXPR_H #define USLDAPEXPR_H #include #include "usSharedData.h" #include #include US_BEGIN_NAMESPACE class Any; class LDAPExprData; class ServicePropertiesImpl; /** * This class is not part of the public API. */ class LDAPExpr { public: - const static int AND; // = 0; - const static int OR; // = 1; - const static int NOT; // = 2; - const static int EQ; // = 4; - const static int LE; // = 8; - const static int GE; // = 16; - const static int APPROX; // = 32; - const static int COMPLEX; // = AND | OR | NOT; - const static int SIMPLE; // = EQ | LE | GE | APPROX; + const static int AND = 0; + const static int OR = 1; + const static int NOT = 2; + const static int EQ = 4; + const static int LE = 8; + const static int GE = 16; + const static int APPROX = 32; + const static int COMPLEX = AND | OR | NOT; + const static int SIMPLE = EQ | LE | GE | APPROX; typedef char Byte; typedef std::vector StringList; typedef std::vector LocalCache; typedef US_UNORDERED_SET_TYPE ObjectClassSet; /** * Creates an invalid LDAPExpr object. Use with care. * * @see IsNull() */ LDAPExpr(); LDAPExpr(const std::string& filter); LDAPExpr(const LDAPExpr& other); LDAPExpr& operator=(const LDAPExpr& other); ~LDAPExpr(); /** * Get object class set matched by this LDAP expression. This will not work * with wildcards and NOT expressions. If a set can not be determined return false. * * \param objClasses The set of matched classes will be added to objClasses. * \return If the set cannot be determined, false is returned, true otherwise. */ bool GetMatchedObjectClasses(ObjectClassSet& objClasses) const; /** * Checks if this LDAP expression is "simple". The definition of * a simple filter is: *

    *
  • (name=value) is simple if * name is a member of the provided keywords, * and value does not contain a wildcard character;
  • *
  • (| EXPR+ ) is simple if all EXPR * expressions are simple;
  • *
  • No other expressions are simple.
  • *
* If the filter is found to be simple, the cache is * filled with mappings from the provided keywords to lists * of attribute values. The keyword-value-pairs are the ones that * satisfy this expression, for the given keywords. * * @param keywords The keywords to look for. * @param cache An array (indexed by the keyword indexes) of lists to * fill in with values saturating this expression. * @return true if this expression is simple, * false otherwise. */ bool IsSimple( const StringList& keywords, LocalCache& cache, bool matchCase) const; /** * Returns true if this instance is invalid, i.e. it was * constructed using LDAPExpr(). * * @return true if the expression is invalid, * false otherwise. */ bool IsNull() const; //! static bool Query(const std::string& filter, const ServicePropertiesImpl& pd); //! Evaluate this LDAP filter. bool Evaluate(const ServicePropertiesImpl& p, bool matchCase) const; //! const std::string ToString() const; private: class ParseState; //! LDAPExpr(int op, const std::vector& args); //! LDAPExpr(int op, const std::string& attrName, const std::string& attrValue); //! static LDAPExpr ParseExpr(ParseState& ps); //! static LDAPExpr ParseSimple(ParseState& ps); static std::string Trim(std::string str); static std::string ToLower(const std::string& str); //! bool Compare(const Any& obj, int op, const std::string& s) const; //! template bool CompareIntegralType(const Any& obj, const int op, const std::string& s) const; //! static bool CompareString(const std::string& s1, int op, const std::string& s2); //! static std::string FixupString(const std::string &s); //! static bool PatSubstr(const std::string& s, const std::string& pat); //! static bool PatSubstr(const std::string& s, int si, const std::string& pat, int pi); - - const static Byte WILDCARD; // = 65535; - const static std::string WILDCARD_STRING;// = std::string( WILDCARD ); - - const static std::string NULLQ; // = "Null query"; - const static std::string GARBAGE; // = "Trailing garbage"; - const static std::string EOS; // = "Unexpected end of query"; - const static std::string MALFORMED; // = "Malformed query"; - const static std::string OPERATOR; // = "Undefined m_operator"; - //! Shared pointer SharedDataPointer d; }; US_END_NAMESPACE #endif // USLDAPEXPR_H diff --git a/Core/CppMicroServices/core/src/service/usServiceEvent.cpp b/Core/CppMicroServices/core/src/service/usServiceEvent.cpp index e1b88f64f4..2e351a4887 100644 --- a/Core/CppMicroServices/core/src/service/usServiceEvent.cpp +++ b/Core/CppMicroServices/core/src/service/usServiceEvent.cpp @@ -1,132 +1,130 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usServiceEvent.h" #include "usServiceProperties.h" US_BEGIN_NAMESPACE class ServiceEventData : public SharedData { public: ServiceEventData(const ServiceEvent::Type& type, const ServiceReferenceBase& reference) : type(type), reference(reference) { } ServiceEventData(const ServiceEventData& other) : SharedData(other), type(other.type), reference(other.reference) { } const ServiceEvent::Type type; const ServiceReferenceBase reference; private: // purposely not implemented ServiceEventData& operator=(const ServiceEventData&); }; ServiceEvent::ServiceEvent() : d(0) { } ServiceEvent::~ServiceEvent() { } bool ServiceEvent::IsNull() const { return !d; } ServiceEvent::ServiceEvent(Type type, const ServiceReferenceBase& reference) : d(new ServiceEventData(type, reference)) { } ServiceEvent::ServiceEvent(const ServiceEvent& other) : d(other.d) { } ServiceEvent& ServiceEvent::operator=(const ServiceEvent& other) { d = other.d; return *this; } ServiceReferenceU ServiceEvent::GetServiceReference() const { return d->reference; } ServiceEvent::Type ServiceEvent::GetType() const { return d->type; } -US_END_NAMESPACE - -US_USE_NAMESPACE - std::ostream& operator<<(std::ostream& os, const ServiceEvent::Type& type) { switch(type) { case ServiceEvent::MODIFIED: return os << "MODIFIED"; case ServiceEvent::MODIFIED_ENDMATCH: return os << "MODIFIED_ENDMATCH"; case ServiceEvent::REGISTERED: return os << "REGISTERED"; case ServiceEvent::UNREGISTERING: return os << "UNREGISTERING"; default: return os << "unknown service event type (" << static_cast(type) << ")"; } } std::ostream& operator<<(std::ostream& os, const ServiceEvent& event) { if (event.IsNull()) return os << "NONE"; os << event.GetType(); ServiceReferenceU sr = event.GetServiceReference(); if (sr) { // Some events will not have a service reference long int sid = any_cast(sr.GetProperty(ServiceConstants::SERVICE_ID())); os << " " << sid; Any classes = sr.GetProperty(ServiceConstants::OBJECTCLASS()); os << " objectClass=" << classes.ToString() << ")"; } return os; } + +US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/service/usServiceHooks.cpp b/Core/CppMicroServices/core/src/service/usServiceHooks.cpp index a34d170a6f..c11d2b4ff4 100644 --- a/Core/CppMicroServices/core/src/service/usServiceHooks.cpp +++ b/Core/CppMicroServices/core/src/service/usServiceHooks.cpp @@ -1,283 +1,286 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usServiceHooks_p.h" #include "usGetModuleContext.h" #include "usCoreModuleContext_p.h" #include "usServiceEventListenerHook.h" #include "usServiceFindHook.h" #include "usServiceListenerHook.h" #include "usServiceReferenceBasePrivate.h" US_BEGIN_NAMESPACE ServiceHooks::ServiceHooks(CoreModuleContext* coreCtx) : coreCtx(coreCtx) , listenerHookTracker(NULL) , bOpen(false) { } ServiceHooks::~ServiceHooks() { this->Close(); } ServiceHooks::TrackedType ServiceHooks::AddingService(const ServiceReferenceType& reference) { ServiceListenerHook* lh = GetModuleContext()->GetService(reference); try { lh->Added(coreCtx->listeners.GetListenerInfoCollection()); } catch (const std::exception& e) { US_WARN << "Failed to call listener hook #" << reference.GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": " << e.what(); } catch (...) { US_WARN << "Failed to call listener hook #" << reference.GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": unknown exception type"; } return lh; } void ServiceHooks::ModifiedService(const ServiceReferenceType& /*reference*/, TrackedType /*service*/) { // noop } void ServiceHooks::RemovedService(const ServiceReferenceType& reference, TrackedType /*service*/) { GetModuleContext()->UngetService(reference); } void ServiceHooks::Open() { US_UNUSED(Lock(this)); listenerHookTracker = new ServiceTracker(GetModuleContext(), this); listenerHookTracker->Open(); bOpen = true; } void ServiceHooks::Close() { US_UNUSED(Lock(this)); - listenerHookTracker->Close(); - delete listenerHookTracker; - listenerHookTracker = NULL; + if (listenerHookTracker) + { + listenerHookTracker->Close(); + delete listenerHookTracker; + listenerHookTracker = NULL; + } bOpen = false; } bool ServiceHooks::IsOpen() const { US_UNUSED(Lock(this)); return bOpen; } void ServiceHooks::FilterServiceReferences(ModuleContext* mc, const std::string& service, const std::string& filter, std::vector& refs) { std::vector srl; coreCtx->services.Get_unlocked(us_service_interface_iid(), srl); if (!srl.empty()) { ShrinkableVector filtered(refs); std::sort(srl.begin(), srl.end()); for (std::vector::reverse_iterator fhrIter = srl.rbegin(), fhrEnd = srl.rend(); fhrIter != fhrEnd; ++fhrIter) { ServiceReference sr = fhrIter->GetReference(); ServiceFindHook* const fh = reinterpret_cast(sr.d->GetService(GetModuleContext()->GetModule())); if (fh != NULL) { try { fh->Find(mc, service, filter, filtered); } catch (const std::exception& e) { US_WARN << "Failed to call find hook #" << sr.GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": " << e.what(); } catch (...) { US_WARN << "Failed to call find hook #" << sr.GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": unknown exception type"; } } } } } void ServiceHooks::FilterServiceEventReceivers(const ServiceEvent& evt, ServiceListeners::ServiceListenerEntries& receivers) { std::vector eventListenerHooks; coreCtx->services.Get_unlocked(us_service_interface_iid(), eventListenerHooks); if (!eventListenerHooks.empty()) { std::sort(eventListenerHooks.begin(), eventListenerHooks.end()); std::map > listeners; for (ServiceListeners::ServiceListenerEntries::iterator sleIter = receivers.begin(), sleEnd = receivers.end(); sleIter != sleEnd; ++sleIter) { listeners[sleIter->GetModuleContext()].push_back(*sleIter); } std::map > shrinkableListeners; for (std::map >::iterator iter = listeners.begin(), iterEnd = listeners.end(); iter != iterEnd; ++iter) { shrinkableListeners.insert(std::make_pair(iter->first, ShrinkableVector(iter->second))); } ShrinkableMap > filtered(shrinkableListeners); for(std::vector::reverse_iterator sriIter = eventListenerHooks.rbegin(), sriEnd = eventListenerHooks.rend(); sriIter != sriEnd; ++sriIter) { ServiceReference sr = sriIter->GetReference(); ServiceEventListenerHook* elh = reinterpret_cast(sr.d->GetService(GetModuleContext()->GetModule())); if(elh != NULL) { try { elh->Event(evt, filtered); } catch(const std::exception& e) { US_WARN << "Failed to call event hook #" << sr.GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": " << e.what(); } catch(...) { US_WARN << "Failed to call event hook #" << sr.GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": unknown exception type"; } } } receivers.clear(); for(std::map >::iterator iter = listeners.begin(), iterEnd = listeners.end(); iter != iterEnd; ++iter) { receivers.insert(iter->second.begin(), iter->second.end()); } } } void ServiceHooks::HandleServiceListenerReg(const ServiceListenerEntry& sle) { if(!IsOpen() || listenerHookTracker->Size() == 0) { return; } std::vector > srl = listenerHookTracker->GetServiceReferences(); if (!srl.empty()) { std::sort(srl.begin(), srl.end()); std::vector set; set.push_back(sle); for (std::vector >::reverse_iterator srIter = srl.rbegin(), srEnd = srl.rend(); srIter != srEnd; ++srIter) { ServiceListenerHook* lh = listenerHookTracker->GetService(*srIter); try { lh->Added(set); } catch (const std::exception& e) { US_WARN << "Failed to call listener hook #" << srIter->GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": " << e.what(); } catch (...) { US_WARN << "Failed to call listener hook #" << srIter->GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": unknown exception"; } } } } void ServiceHooks::HandleServiceListenerUnreg(const ServiceListenerEntry& sle) { if(IsOpen()) { std::vector set; set.push_back(sle); HandleServiceListenerUnreg(set); } } void ServiceHooks::HandleServiceListenerUnreg(const std::vector& set) { if(!IsOpen() || listenerHookTracker->Size() == 0) { return; } std::vector > srl = listenerHookTracker->GetServiceReferences(); if (!srl.empty()) { std::vector lis; for (std::vector::const_iterator sleIter = set.begin(), sleEnd = set.end(); sleIter != sleEnd; ++sleIter) { lis.push_back(*sleIter); } std::sort(srl.begin(), srl.end()); for (std::vector >::reverse_iterator srIter = srl.rbegin(), srEnd = srl.rend(); srIter != srEnd; ++srIter) { ServiceListenerHook* const lh = listenerHookTracker->GetService(*srIter); try { lh->Removed(lis); } catch (const std::exception& e) { US_WARN << "Failed to call listener hook #" << srIter->GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": " << e.what(); } catch (...) { US_WARN << "Failed to call listener hook #" << srIter->GetProperty(ServiceConstants::SERVICE_ID()).ToString() << ": unknown exception type"; } } } } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/service/usServiceListenerHook_p.h b/Core/CppMicroServices/core/src/service/usServiceListenerHook_p.h index dfc84dc011..083a693ec3 100644 --- a/Core/CppMicroServices/core/src/service/usServiceListenerHook_p.h +++ b/Core/CppMicroServices/core/src/service/usServiceListenerHook_p.h @@ -1,43 +1,48 @@ -/*=================================================================== +/*============================================================================= -BlueBerry Platform + Library: CppMicroServices -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -See LICENSE.txt or http://www.mitk.org for details. + http://www.apache.org/licenses/LICENSE-2.0 -===================================================================*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ #ifndef USSERVICELISTENERHOOK_P_H #define USSERVICELISTENERHOOK_P_H #include "usServiceListenerHook.h" #include "usServiceListenerEntry_p.h" #include "usSharedData.h" US_BEGIN_NAMESPACE class ServiceListenerHook::ListenerInfoData : public SharedData { public: ListenerInfoData(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& l, void* data, const std::string& filter); virtual ~ListenerInfoData(); ModuleContext* const mc; ServiceListenerEntry::ServiceListener listener; void* data; std::string filter; bool bRemoved; }; US_END_NAMESPACE #endif // USSERVICELISTENERHOOK_P_H diff --git a/Core/CppMicroServices/core/src/service/usServiceListeners.cpp b/Core/CppMicroServices/core/src/service/usServiceListeners.cpp index f86ee2ba3c..39217e79ea 100644 --- a/Core/CppMicroServices/core/src/service/usServiceListeners.cpp +++ b/Core/CppMicroServices/core/src/service/usServiceListeners.cpp @@ -1,329 +1,334 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usUtils_p.h" #include "usServiceListeners_p.h" #include "usServiceReferenceBasePrivate.h" #include "usCoreModuleContext_p.h" #include "usModule.h" #include "usModuleContext.h" US_BEGIN_NAMESPACE const int ServiceListeners::OBJECTCLASS_IX = 0; const int ServiceListeners::SERVICE_ID_IX = 1; ServiceListeners::ServiceListeners(CoreModuleContext* coreCtx) : coreCtx(coreCtx) { hashedServiceKeys.push_back(ServiceConstants::OBJECTCLASS()); hashedServiceKeys.push_back(ServiceConstants::SERVICE_ID()); } void ServiceListeners::AddServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener, void* data, const std::string& filter) { US_UNUSED(Lock(this)); ServiceListenerEntry sle(mc, listener, data, filter); RemoveServiceListener_unlocked(sle); serviceSet.insert(sle); coreCtx->serviceHooks.HandleServiceListenerReg(sle); CheckSimple(sle); } void ServiceListeners::RemoveServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener, void* data) { ServiceListenerEntry entryToRemove(mc, listener, data); US_UNUSED(Lock(this)); RemoveServiceListener_unlocked(entryToRemove); } void ServiceListeners::RemoveServiceListener_unlocked(const ServiceListenerEntry& entryToRemove) { ServiceListenerEntries::const_iterator it = serviceSet.find(entryToRemove); if (it != serviceSet.end()) { it->SetRemoved(true); coreCtx->serviceHooks.HandleServiceListenerUnreg(*it); RemoveFromCache(*it); serviceSet.erase(it); } } void ServiceListeners::AddModuleListener(ModuleContext* mc, const ModuleListener& listener, void* data) { MutexLock lock(moduleListenerMapMutex); ModuleListenerMap::value_type::second_type& listeners = moduleListenerMap[mc]; if (std::find_if(listeners.begin(), listeners.end(), std::bind1st(ModuleListenerCompare(), std::make_pair(listener, data))) == listeners.end()) { listeners.push_back(std::make_pair(listener, data)); } } void ServiceListeners::RemoveModuleListener(ModuleContext* mc, const ModuleListener& listener, void* data) { MutexLock lock(moduleListenerMapMutex); moduleListenerMap[mc].remove_if(std::bind1st(ModuleListenerCompare(), std::make_pair(listener, data))); } void ServiceListeners::ModuleChanged(const ModuleEvent& evt) { ModuleListenerMap filteredModuleListeners; coreCtx->moduleHooks.FilterModuleEventReceivers(evt, filteredModuleListeners); for(ModuleListenerMap::iterator iter = filteredModuleListeners.begin(), end = filteredModuleListeners.end(); iter != end; ++iter) { for (ModuleListenerMap::mapped_type::iterator iter2 = iter->second.begin(), end2 = iter->second.end(); iter2 != end2; ++iter2) { - (iter2->first)(evt); + try + { + (iter2->first)(evt); + } + catch (const std::exception& e) + { + US_WARN << "Module listener threw an exception: " << e.what(); + } } } } void ServiceListeners::RemoveAllListeners(ModuleContext* mc) { { US_UNUSED(Lock(this)); for (ServiceListenerEntries::iterator it = serviceSet.begin(); it != serviceSet.end(); ) { if (it->GetModuleContext() == mc) { RemoveFromCache(*it); serviceSet.erase(it++); } else { ++it; } } } { MutexLock lock(moduleListenerMapMutex); moduleListenerMap.erase(mc); } } void ServiceListeners::HooksModuleStopped(ModuleContext* mc) { US_UNUSED(Lock(this)); std::vector entries; for (ServiceListenerEntries::iterator it = serviceSet.begin(); it != serviceSet.end(); ) { if (it->GetModuleContext() == mc) { entries.push_back(*it); } } coreCtx->serviceHooks.HandleServiceListenerUnreg(entries); } void ServiceListeners::ServiceChanged(ServiceListenerEntries& receivers, const ServiceEvent& evt) { ServiceListenerEntries matchBefore; ServiceChanged(receivers, evt, matchBefore); } void ServiceListeners::ServiceChanged(ServiceListenerEntries& receivers, const ServiceEvent& evt, ServiceListenerEntries& matchBefore) { int n = 0; if (!matchBefore.empty()) { for (ServiceListenerEntries::const_iterator l = receivers.begin(); l != receivers.end(); ++l) { matchBefore.erase(*l); } } for (ServiceListenerEntries::const_iterator l = receivers.begin(); l != receivers.end(); ++l) { if (!l->IsRemoved()) { try { ++n; l->CallDelegate(evt); } catch (...) { US_WARN << "Service listener" - #ifdef US_MODULE_SUPPORT_ENABLED - << " in " << l->GetModule()->GetName() - #endif + << " in " << l->GetModuleContext()->GetModule()->GetName() << " threw an exception!"; } } } //US_DEBUG << "Notified " << n << " listeners"; } void ServiceListeners::GetMatchingServiceListeners(const ServiceEvent& evt, ServiceListenerEntries& set, bool lockProps) { US_UNUSED(Lock(this)); // Filter the original set of listeners ServiceListenerEntries receivers = serviceSet; coreCtx->serviceHooks.FilterServiceEventReceivers(evt, receivers); // Check complicated or empty listener filters for (std::list::const_iterator sse = complicatedListeners.begin(); sse != complicatedListeners.end(); ++sse) { if (receivers.count(*sse) == 0) continue; const LDAPExpr& ldapExpr = sse->GetLDAPExpr(); if (ldapExpr.IsNull() || ldapExpr.Evaluate(evt.GetServiceReference().d->GetProperties(), false)) { set.insert(*sse); } } //US_DEBUG << "Added " << set.size() << " out of " << n // << " listeners with complicated filters"; // Check the cache const std::vector c(any_cast > (evt.GetServiceReference().d->GetProperty(ServiceConstants::OBJECTCLASS(), lockProps))); for (std::vector::const_iterator objClass = c.begin(); objClass != c.end(); ++objClass) { AddToSet(set, receivers, OBJECTCLASS_IX, *objClass); } long service_id = any_cast(evt.GetServiceReference().d->GetProperty(ServiceConstants::SERVICE_ID(), lockProps)); std::stringstream ss; ss << service_id; AddToSet(set, receivers, SERVICE_ID_IX, ss.str()); } std::vector ServiceListeners::GetListenerInfoCollection() const { US_UNUSED(Lock(this)); std::vector result; result.reserve(serviceSet.size()); for (ServiceListenerEntries::const_iterator iter = serviceSet.begin(), iterEnd = serviceSet.end(); iter != iterEnd; ++iter) { result.push_back(*iter); } return result; } void ServiceListeners::RemoveFromCache(const ServiceListenerEntry& sle) { if (!sle.GetLocalCache().empty()) { for (std::size_t i = 0; i < hashedServiceKeys.size(); ++i) { CacheType& keymap = cache[i]; std::vector& l = sle.GetLocalCache()[i]; for (std::vector::const_iterator it = l.begin(); it != l.end(); ++it) { std::list& sles = keymap[*it]; sles.remove(sle); if (sles.empty()) { keymap.erase(*it); } } } } else { complicatedListeners.remove(sle); } } void ServiceListeners::CheckSimple(const ServiceListenerEntry& sle) { if (sle.GetLDAPExpr().IsNull()) { complicatedListeners.push_back(sle); } else { LDAPExpr::LocalCache local_cache; if (sle.GetLDAPExpr().IsSimple(hashedServiceKeys, local_cache, false)) { sle.GetLocalCache() = local_cache; for (std::size_t i = 0; i < hashedServiceKeys.size(); ++i) { for (std::vector::const_iterator it = local_cache[i].begin(); it != local_cache[i].end(); ++it) { std::list& sles = cache[i][*it]; sles.push_back(sle); } } } else { //US_DEBUG << "Too complicated filter: " << sle.GetFilter(); complicatedListeners.push_back(sle); } } } void ServiceListeners::AddToSet(ServiceListenerEntries& set, const ServiceListenerEntries& receivers, int cache_ix, const std::string& val) { std::list& l = cache[cache_ix][val]; if (!l.empty()) { //US_DEBUG << hashedServiceKeys[cache_ix] << " matches " << l.size(); for (std::list::const_iterator entry = l.begin(); entry != l.end(); ++entry) { if (receivers.count(*entry)) { set.insert(*entry); } } } else { //US_DEBUG << hashedServiceKeys[cache_ix] << " matches none"; } } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/service/usServiceReferenceBase.cpp b/Core/CppMicroServices/core/src/service/usServiceReferenceBase.cpp index f785148c74..0a57413fa3 100644 --- a/Core/CppMicroServices/core/src/service/usServiceReferenceBase.cpp +++ b/Core/CppMicroServices/core/src/service/usServiceReferenceBase.cpp @@ -1,215 +1,221 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usServiceReferenceBase.h" #include "usServiceReferenceBasePrivate.h" #include "usServiceRegistrationBasePrivate.h" #include "usModule.h" #include "usModulePrivate.h" +#include US_BEGIN_NAMESPACE ServiceReferenceBase::ServiceReferenceBase() : d(new ServiceReferenceBasePrivate(0)) { } ServiceReferenceBase::ServiceReferenceBase(const ServiceReferenceBase& ref) : d(ref.d) { d->ref.Ref(); } ServiceReferenceBase::ServiceReferenceBase(ServiceRegistrationBasePrivate* reg) : d(new ServiceReferenceBasePrivate(reg)) { } void ServiceReferenceBase::SetInterfaceId(const std::string& interfaceId) { if (d->ref > 1) { // detach d->ref.Deref(); d = new ServiceReferenceBasePrivate(d->registration); } d->interfaceId = interfaceId; } ServiceReferenceBase::operator bool_type() const { return GetModule() != 0 ? &ServiceReferenceBase::d : NULL; } ServiceReferenceBase& ServiceReferenceBase::operator=(int null) { if (null == 0) { if (!d->ref.Deref()) delete d; d = new ServiceReferenceBasePrivate(0); } return *this; } ServiceReferenceBase::~ServiceReferenceBase() { if (!d->ref.Deref()) delete d; } Any ServiceReferenceBase::GetProperty(const std::string& key) const { MutexLock lock(d->registration->propsLock); return d->registration->properties.Value(key); } void ServiceReferenceBase::GetPropertyKeys(std::vector& keys) const { MutexLock lock(d->registration->propsLock); const std::vector& ks = d->registration->properties.Keys(); keys.assign(ks.begin(), ks.end()); } Module* ServiceReferenceBase::GetModule() const { if (d->registration == 0 || d->registration->module == 0) { return 0; } return d->registration->module->q; } void ServiceReferenceBase::GetUsingModules(std::vector& modules) const { MutexLock lock(d->registration->propsLock); ServiceRegistrationBasePrivate::ModuleToRefsMap::const_iterator end = d->registration->dependents.end(); for (ServiceRegistrationBasePrivate::ModuleToRefsMap::const_iterator iter = d->registration->dependents.begin(); iter != end; ++iter) { modules.push_back(iter->first); } } bool ServiceReferenceBase::operator<(const ServiceReferenceBase& reference) const { - int r1 = 0; - int r2 = 0; - if (!(*this)) { return true; } if (!reference) { return false; } - Any anyR1 = GetProperty(ServiceConstants::SERVICE_RANKING()); - Any anyR2 = reference.GetProperty(ServiceConstants::SERVICE_RANKING()); - if (anyR1.Type() == typeid(int)) r1 = any_cast(anyR1); - if (anyR2.Type() == typeid(int)) r2 = any_cast(anyR2); + const Any anyR1 = GetProperty(ServiceConstants::SERVICE_RANKING()); + const Any anyR2 = reference.GetProperty(ServiceConstants::SERVICE_RANKING()); + assert(anyR1.Empty() || anyR1.Type() == typeid(int)); + assert(anyR2.Empty() || anyR2.Type() == typeid(int)); + const int r1 = anyR1.Empty() ? 0 : *any_cast(&anyR1); + const int r2 = anyR2.Empty() ? 0 : *any_cast(&anyR2); if (r1 != r2) { // use ranking if ranking differs return r1 < r2; } else { - long int id1 = any_cast(GetProperty(ServiceConstants::SERVICE_ID())); - long int id2 = any_cast(reference.GetProperty(ServiceConstants::SERVICE_ID())); + const Any anyId1 = GetProperty(ServiceConstants::SERVICE_ID()); + const Any anyId2 = reference.GetProperty(ServiceConstants::SERVICE_ID()); + assert(anyId1.Type() == typeid(long int)); + assert(anyId2.Type() == typeid(long int)); + const long int id1 = *any_cast(&anyId1); + const long int id2 = *any_cast(&anyId2); // otherwise compare using IDs, // is less than if it has a higher ID. return id2 < id1; } } bool ServiceReferenceBase::operator==(const ServiceReferenceBase& reference) const { return d->registration == reference.d->registration; } ServiceReferenceBase& ServiceReferenceBase::operator=(const ServiceReferenceBase& reference) { ServiceReferenceBasePrivate* curr_d = d; d = reference.d; d->ref.Ref(); if (!curr_d->ref.Deref()) delete curr_d; return *this; } bool ServiceReferenceBase::IsConvertibleTo(const std::string& interfaceId) const { return d->IsConvertibleTo(interfaceId); } std::string ServiceReferenceBase::GetInterfaceId() const { return d->interfaceId; } std::size_t ServiceReferenceBase::Hash() const { using namespace US_HASH_FUNCTION_NAMESPACE; return US_HASH_FUNCTION(ServiceRegistrationBasePrivate*, this->d->registration); } US_END_NAMESPACE US_USE_NAMESPACE std::ostream& operator<<(std::ostream& os, const ServiceReferenceBase& serviceRef) { if (serviceRef) { + assert(serviceRef.GetModule() != NULL); + os << "Reference for service object registered from " << serviceRef.GetModule()->GetName() << " " << serviceRef.GetModule()->GetVersion() << " ("; std::vector keys; serviceRef.GetPropertyKeys(keys); size_t keySize = keys.size(); for(size_t i = 0; i < keySize; ++i) { os << keys[i] << "=" << serviceRef.GetProperty(keys[i]).ToString(); if (i < keySize-1) os << ","; } os << ")"; } else { os << "Invalid service reference"; } return os; } diff --git a/Core/CppMicroServices/core/src/service/usServiceRegistry.cpp b/Core/CppMicroServices/core/src/service/usServiceRegistry.cpp index c6df274ef1..e6e67d88ba 100644 --- a/Core/CppMicroServices/core/src/service/usServiceRegistry.cpp +++ b/Core/CppMicroServices/core/src/service/usServiceRegistry.cpp @@ -1,336 +1,338 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include +#include #include "usServiceRegistry_p.h" #include "usServiceFactory.h" #include "usPrototypeServiceFactory.h" #include "usServiceRegistry_p.h" #include "usServiceRegistrationBasePrivate.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" US_BEGIN_NAMESPACE ServicePropertiesImpl ServiceRegistry::CreateServiceProperties(const ServiceProperties& in, const std::vector& classes, bool isFactory, bool isPrototypeFactory, long sid) { static long nextServiceID = 1; ServiceProperties props(in); if (!classes.empty()) { props.insert(std::make_pair(ServiceConstants::OBJECTCLASS(), classes)); } props.insert(std::make_pair(ServiceConstants::SERVICE_ID(), sid != -1 ? sid : nextServiceID++)); if (isPrototypeFactory) { props.insert(std::make_pair(ServiceConstants::SERVICE_SCOPE(), ServiceConstants::SCOPE_PROTOTYPE())); } else if (isFactory) { props.insert(std::make_pair(ServiceConstants::SERVICE_SCOPE(), ServiceConstants::SCOPE_MODULE())); } else { props.insert(std::make_pair(ServiceConstants::SERVICE_SCOPE(), ServiceConstants::SCOPE_SINGLETON())); } return ServicePropertiesImpl(props); } ServiceRegistry::ServiceRegistry(CoreModuleContext* coreCtx) : core(coreCtx) { } ServiceRegistry::~ServiceRegistry() { Clear(); } void ServiceRegistry::Clear() { services.clear(); serviceRegistrations.clear(); classServices.clear(); core = 0; } ServiceRegistrationBase ServiceRegistry::RegisterService(ModulePrivate* module, const InterfaceMap& service, const ServiceProperties& properties) { if (service.empty()) { throw std::invalid_argument("Can't register empty InterfaceMap as a service"); } // Check if we got a service factory bool isFactory = service.count("org.cppmicroservices.factory") > 0; bool isPrototypeFactory = (isFactory ? dynamic_cast(reinterpret_cast(service.find("org.cppmicroservices.factory")->second)) != NULL : false); std::vector classes; // Check if service implements claimed classes and that they exist. for (InterfaceMap::const_iterator i = service.begin(); i != service.end(); ++i) { if (i->first.empty() || (!isFactory && i->second == NULL)) { throw std::invalid_argument("Can't register as null class"); } classes.push_back(i->first); } ServiceRegistrationBase res(module, service, CreateServiceProperties(properties, classes, isFactory, isPrototypeFactory)); { MutexLock lock(mutex); services.insert(std::make_pair(res, classes)); serviceRegistrations.push_back(res); for (std::vector::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::vector& s = classServices[*i]; std::vector::iterator ip = std::lower_bound(s.begin(), s.end(), res); s.insert(ip, res); } } ServiceReferenceBase r = res.GetReference(std::string()); ServiceListeners::ServiceListenerEntries listeners; ServiceEvent registeredEvent(ServiceEvent::REGISTERED, r); module->coreCtx->listeners.GetMatchingServiceListeners(registeredEvent, listeners); module->coreCtx->listeners.ServiceChanged(listeners, registeredEvent); return res; } void ServiceRegistry::UpdateServiceRegistrationOrder(const ServiceRegistrationBase& sr, const std::vector& classes) { MutexLock lock(mutex); for (std::vector::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::vector& s = classServices[*i]; s.erase(std::remove(s.begin(), s.end(), sr), s.end()); s.insert(std::lower_bound(s.begin(), s.end(), sr), sr); } } void ServiceRegistry::Get(const std::string& clazz, std::vector& serviceRegs) const { MutexLock lock(mutex); Get_unlocked(clazz, serviceRegs); } void ServiceRegistry::Get_unlocked(const std::string& clazz, std::vector& serviceRegs) const { MapClassServices::const_iterator i = classServices.find(clazz); if (i != classServices.end()) { serviceRegs = i->second; } } ServiceReferenceBase ServiceRegistry::Get(ModulePrivate* module, const std::string& clazz) const { MutexLock lock(mutex); try { std::vector srs; Get_unlocked(clazz, "", module, srs); US_DEBUG << "get service ref " << clazz << " for module " << module->info.name << " = " << srs.size() << " refs"; if (!srs.empty()) { return srs.back(); } } catch (const std::invalid_argument& ) { } return ServiceReferenceBase(); } void ServiceRegistry::Get(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::vector& res) const { MutexLock lock(mutex); Get_unlocked(clazz, filter, module, res); } void ServiceRegistry::Get_unlocked(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::vector& res) const { std::vector::const_iterator s; std::vector::const_iterator send; std::vector v; LDAPExpr ldap; if (clazz.empty()) { if (!filter.empty()) { ldap = LDAPExpr(filter); LDAPExpr::ObjectClassSet matched; if (ldap.GetMatchedObjectClasses(matched)) { v.clear(); for(LDAPExpr::ObjectClassSet::const_iterator className = matched.begin(); className != matched.end(); ++className) { MapClassServices::const_iterator i = classServices.find(*className); if (i != classServices.end()) { std::copy(i->second.begin(), i->second.end(), std::back_inserter(v)); } } if (!v.empty()) { s = v.begin(); send = v.end(); } else { return; } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { MapClassServices::const_iterator it = classServices.find(clazz); if (it != classServices.end()) { s = it->second.begin(); send = it->second.end(); } else { return; } if (!filter.empty()) { ldap = LDAPExpr(filter); } } for (; s != send; ++s) { ServiceReferenceBase sri = s->GetReference(clazz); if (filter.empty() || ldap.Evaluate(s->d->properties, false)) { res.push_back(sri); } } if (!res.empty()) { if (module != NULL) { core->serviceHooks.FilterServiceReferences(module->moduleContext, clazz, filter, res); } else { core->serviceHooks.FilterServiceReferences(NULL, clazz, filter, res); } } } void ServiceRegistry::RemoveServiceRegistration(const ServiceRegistrationBase& sr) { MutexLock lock(mutex); + assert(sr.d->properties.Value(ServiceConstants::OBJECTCLASS()).Type() == typeid(std::vector)); const std::vector& classes = ref_any_cast >( sr.d->properties.Value(ServiceConstants::OBJECTCLASS())); services.erase(sr); serviceRegistrations.erase(std::remove(serviceRegistrations.begin(), serviceRegistrations.end(), sr), serviceRegistrations.end()); for (std::vector::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::vector& s = classServices[*i]; if (s.size() > 1) { s.erase(std::remove(s.begin(), s.end(), sr), s.end()); } else { classServices.erase(*i); } } } void ServiceRegistry::GetRegisteredByModule(ModulePrivate* p, std::vector& res) const { MutexLock lock(mutex); for (std::vector::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->module == p) { res.push_back(*i); } } } void ServiceRegistry::GetUsedByModule(Module* p, std::vector& res) const { MutexLock lock(mutex); for (std::vector::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->IsUsedByModule(p)) { res.push_back(*i); } } } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/service/usServiceTracker.tpp b/Core/CppMicroServices/core/src/service/usServiceTracker.tpp index abc01853d3..e03355c012 100644 --- a/Core/CppMicroServices/core/src/service/usServiceTracker.tpp +++ b/Core/CppMicroServices/core/src/service/usServiceTracker.tpp @@ -1,463 +1,464 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usServiceTrackerPrivate.h" #include "usTrackedService_p.h" #include "usServiceException.h" #include "usModuleContext.h" +#include #include #include US_BEGIN_NAMESPACE template ServiceTracker::~ServiceTracker() { Close(); delete d; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4355) #endif template ServiceTracker::ServiceTracker(ModuleContext* context, const ServiceReferenceType& reference, _ServiceTrackerCustomizer* customizer) : d(new _ServiceTrackerPrivate(this, context, reference, customizer)) { } template ServiceTracker::ServiceTracker(ModuleContext* context, const std::string& clazz, _ServiceTrackerCustomizer* customizer) : d(new _ServiceTrackerPrivate(this, context, clazz, customizer)) { } template ServiceTracker::ServiceTracker(ModuleContext* context, const LDAPFilter& filter, _ServiceTrackerCustomizer* customizer) : d(new _ServiceTrackerPrivate(this, context, filter, customizer)) { } template ServiceTracker::ServiceTracker(ModuleContext *context, _ServiceTrackerCustomizer* customizer) : d(new _ServiceTrackerPrivate(this, context, us_service_interface_iid(), customizer)) { - const char* clazz = us_service_interface_iid(); - if (clazz == 0) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); + std::string clazz = us_service_interface_iid(); + if (clazz.empty()) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); } #ifdef _MSC_VER #pragma warning(pop) #endif template void ServiceTracker::Open() { _TrackedService* t; { US_UNUSED(typename _ServiceTrackerPrivate::Lock(d)); if (d->trackedService) { return; } US_DEBUG(d->DEBUG_OUTPUT) << "ServiceTracker::Open: " << d->filter; t = new _TrackedService(this, d->customizer); { US_UNUSED(typename _TrackedService::Lock(*t)); try { d->context->AddServiceListener(t, &_TrackedService::ServiceChanged, d->listenerFilter); std::vector references; if (!d->trackClass.empty()) { references = d->GetInitialReferences(d->trackClass, std::string()); } else { if (d->trackReference.GetModule() != 0) { references.push_back(d->trackReference); } else { /* user supplied filter */ references = d->GetInitialReferences(std::string(), (d->listenerFilter.empty()) ? d->filter.ToString() : d->listenerFilter); } } /* set tracked with the initial references */ t->SetInitial(references); } catch (const std::invalid_argument& e) { throw std::runtime_error(std::string("unexpected std::invalid_argument exception: ") + e.what()); } } d->trackedService = t; } /* Call tracked outside of synchronized region */ t->TrackInitial(); /* process the initial references */ } template void ServiceTracker::Close() { _TrackedService* outgoing; std::vector references; { US_UNUSED(typename _ServiceTrackerPrivate::Lock(d)); outgoing = d->trackedService; if (outgoing == 0) { return; } US_DEBUG(d->DEBUG_OUTPUT) << "ServiceTracker::close:" << d->filter; outgoing->Close(); references = GetServiceReferences(); d->trackedService = 0; try { d->context->RemoveServiceListener(outgoing, &_TrackedService::ServiceChanged); } catch (const std::logic_error& /*e*/) { /* In case the context was stopped. */ } } d->Modified(); /* clear the cache */ { US_UNUSED(typename _TrackedService::Lock(outgoing)); outgoing->NotifyAll(); /* wake up any waiters */ } for(typename std::vector::const_iterator ref = references.begin(); ref != references.end(); ++ref) { outgoing->Untrack(*ref, ServiceEvent()); } if (d->DEBUG_OUTPUT) { US_UNUSED(typename _ServiceTrackerPrivate::Lock(d)); if ((d->cachedReference.GetModule() == 0) && !TTT::IsValid(d->cachedService)) { US_DEBUG(true) << "ServiceTracker::close[cached cleared]:" << d->filter; } } delete outgoing; d->trackedService = 0; } template typename ServiceTracker::T ServiceTracker::WaitForService(unsigned long timeoutMillis) { T object = GetService(); while (!TTT::IsValid(object)) { _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return TTT::DefaultValue(); } { US_UNUSED(typename _TrackedService::Lock(t)); if (t->Size() == 0) { t->Wait(timeoutMillis); } } object = GetService(); } return object; } template std::vector::ServiceReferenceType> ServiceTracker::GetServiceReferences() const { std::vector refs; _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return refs; } { US_UNUSED(typename _TrackedService::Lock(t)); d->GetServiceReferences_unlocked(refs, t); } return refs; } template typename ServiceTracker::ServiceReferenceType ServiceTracker::GetServiceReference() const { ServiceReferenceType reference; { US_UNUSED(typename _ServiceTrackerPrivate::Lock(d)); reference = d->cachedReference; } if (reference.GetModule() != 0) { US_DEBUG(d->DEBUG_OUTPUT) << "ServiceTracker::getServiceReference[cached]:" << d->filter; return reference; } US_DEBUG(d->DEBUG_OUTPUT) << "ServiceTracker::getServiceReference:" << d->filter; std::vector references = GetServiceReferences(); std::size_t length = references.size(); if (length == 0) { /* if no service is being tracked */ throw ServiceException("No service is being tracked"); } typename std::vector::const_iterator selectedRef = references.begin(); if (length > 1) { /* if more than one service, select highest ranking */ std::vector rankings(length); int count = 0; int maxRanking = std::numeric_limits::min(); typename std::vector::const_iterator refIter = references.begin(); for (std::size_t i = 0; i < length; i++) { Any rankingAny = refIter->GetProperty(ServiceConstants::SERVICE_RANKING()); int ranking = 0; if (rankingAny.Type() == typeid(int)) { ranking = any_cast(rankingAny); } rankings[i] = ranking; if (ranking > maxRanking) { selectedRef = refIter; maxRanking = ranking; count = 1; } else { if (ranking == maxRanking) { count++; } } ++refIter; } if (count > 1) { /* if still more than one service, select lowest id */ long int minId = std::numeric_limits::max(); refIter = references.begin(); for (std::size_t i = 0; i < length; i++) { if (rankings[i] == maxRanking) { Any idAny = refIter->GetProperty(ServiceConstants::SERVICE_ID()); long int id = 0; if (idAny.Type() == typeid(long int)) { id = any_cast(idAny); } if (id < minId) { selectedRef = refIter; minId = id; } } ++refIter; } } } { US_UNUSED(typename _ServiceTrackerPrivate::Lock(d)); d->cachedReference = *selectedRef; return d->cachedReference; } } template typename ServiceTracker::T ServiceTracker::GetService(const ServiceReferenceType& reference) const { _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return TTT::DefaultValue(); } { US_UNUSED(typename _TrackedService::Lock(t)); return t->GetCustomizedObject(reference); } } template std::vector::T> ServiceTracker::GetServices() const { std::vector services; _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return services; } { US_UNUSED(typename _TrackedService::Lock(t)); std::vector references; d->GetServiceReferences_unlocked(references, t); for(typename std::vector::const_iterator ref = references.begin(); ref != references.end(); ++ref) { services.push_back(t->GetCustomizedObject(*ref)); } } return services; } template typename ServiceTracker::T ServiceTracker::GetService() const { { US_UNUSED(typename _ServiceTrackerPrivate::Lock(d)); const T& service = d->cachedService; if (TTT::IsValid(service)) { US_DEBUG(d->DEBUG_OUTPUT) << "ServiceTracker::getService[cached]:" << d->filter; return service; } } US_DEBUG(d->DEBUG_OUTPUT) << "ServiceTracker::getService:" << d->filter; try { ServiceReferenceType reference = GetServiceReference(); if (reference.GetModule() == 0) { return TTT::DefaultValue(); } { US_UNUSED(typename _ServiceTrackerPrivate::Lock(d)); return d->cachedService = GetService(reference); } } catch (const ServiceException&) { return TTT::DefaultValue(); } } template void ServiceTracker::Remove(const ServiceReferenceType& reference) { _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return; } t->Untrack(reference, ServiceEvent()); } template int ServiceTracker::Size() const { _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return 0; } { US_UNUSED(typename _TrackedService::Lock(t)); return static_cast(t->Size()); } } template int ServiceTracker::GetTrackingCount() const { _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return -1; } { US_UNUSED(typename _TrackedService::Lock(t)); return t->GetTrackingCount(); } } template void ServiceTracker::GetTracked(TrackingMap& map) const { _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return; } { US_UNUSED(typename _TrackedService::Lock(t)); t->CopyEntries(map); } } template bool ServiceTracker::IsEmpty() const { _TrackedService* t = d->Tracked(); if (t == 0) { /* if ServiceTracker is not open */ return true; } { US_UNUSED(typename _TrackedService::Lock(t)); return t->IsEmpty(); } } template typename ServiceTracker::T ServiceTracker::AddingService(const ServiceReferenceType& reference) { return TTT::ConvertToTrackedType(d->context->GetService(reference)); } template void ServiceTracker::ModifiedService(const ServiceReferenceType& /*reference*/, T /*service*/) { /* do nothing */ } template void ServiceTracker::RemovedService(const ServiceReferenceType& reference, T /*service*/) { d->context->UngetService(reference); } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/util/tinfl.c b/Core/CppMicroServices/core/src/util/tinfl.c deleted file mode 100644 index a17a156b6c..0000000000 --- a/Core/CppMicroServices/core/src/util/tinfl.c +++ /dev/null @@ -1,592 +0,0 @@ -/* tinfl.c v1.11 - public domain inflate with zlib header parsing/adler32 checking (inflate-only subset of miniz.c) - See "unlicense" statement at the end of this file. - Rich Geldreich , last updated May 20, 2011 - Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt - - The entire decompressor coroutine is implemented in tinfl_decompress(). The other functions are optional high-level helpers. -*/ -#ifndef TINFL_HEADER_INCLUDED -#define TINFL_HEADER_INCLUDED - -#include - -typedef unsigned char mz_uint8; -typedef signed short mz_int16; -typedef unsigned short mz_uint16; -typedef unsigned int mz_uint32; -typedef unsigned int mz_uint; -typedef unsigned long long mz_uint64; - -#if defined(_M_IX86) || defined(_M_X64) -// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster). -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 -// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. -#define MINIZ_LITTLE_ENDIAN 1 -#endif - -#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) -// Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator) -#define MINIZ_HAS_64BIT_REGISTERS 1 -#endif - -// Works around MSVC's spammy "warning C4127: conditional expression is constant" message. -#ifdef _MSC_VER - #define MZ_MACRO_END while (0, 0) -#else - #define MZ_MACRO_END while (0) -#endif - -// Decompression flags used by tinfl_decompress(). -// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. -// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. -// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). -// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. -enum -{ - TINFL_FLAG_PARSE_ZLIB_HEADER = 1, - TINFL_FLAG_HAS_MORE_INPUT = 2, - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, - TINFL_FLAG_COMPUTE_ADLER32 = 8 -}; - -// High level decompression functions: -// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. -// On return: -// Function returns a pointer to the decompressed data, or NULL on failure. -// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must free() the returned block when it's no longer needed. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. -// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. -#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. -// Returns 1 on success or 0 on failure. -typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; - -// Max size of LZ dictionary. -#define TINFL_LZ_DICT_SIZE 32768 - -// Return status. -typedef enum -{ - TINFL_STATUS_BAD_PARAM = -3, - TINFL_STATUS_ADLER32_MISMATCH = -2, - TINFL_STATUS_FAILED = -1, - TINFL_STATUS_DONE = 0, - TINFL_STATUS_NEEDS_MORE_INPUT = 1, - TINFL_STATUS_HAS_MORE_OUTPUT = 2 -} tinfl_status; - -// Initializes the decompressor to its initial state. -#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END -#define tinfl_get_adler32(r) (r)->m_check_adler32 - -// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. -// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); - -// Internal/private bits follow. -enum -{ - TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, - TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS -}; - -typedef struct -{ - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; -} tinfl_huff_table; - -#if MINIZ_HAS_64BIT_REGISTERS - #define TINFL_USE_64BIT_BITBUF 1 -#endif - -#if TINFL_USE_64BIT_BITBUF - typedef mz_uint64 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (64) -#else - typedef mz_uint32 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (32) -#endif - -struct tinfl_decompressor_tag -{ - mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; - tinfl_bit_buf_t m_bit_buf; - size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; - mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; -}; - -#endif // #ifdef TINFL_HEADER_INCLUDED - -// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) - -#ifndef TINFL_HEADER_FILE_ONLY - -#include - -// MZ_MALLOC, etc. are only used by the optional high-level helper functions. -#ifdef MINIZ_NO_MALLOC - #define MZ_MALLOC(x) NULL - #define MZ_FREE(x) x, ((void)0) - #define MZ_REALLOC(p, x) NULL -#else - #define MZ_MALLOC(x) malloc(x) - #define MZ_FREE(x) free(x) - #define MZ_REALLOC(p, x) realloc(p, x) -#endif - -#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) -#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) -#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) - #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) -#else - #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) - #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) -#endif - -#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) -#define TINFL_MEMSET(p, c, l) memset(p, c, l) - -#define TINFL_CR_BEGIN switch(r->m_state) { case 0: -#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END -#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END -#define TINFL_CR_FINISH } - -// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never -// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. -#define TINFL_GET_BYTE(state_index, c) do { \ - if (pIn_buf_cur >= pIn_buf_end) { \ - for ( ; ; ) { \ - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ - TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ - if (pIn_buf_cur < pIn_buf_end) { \ - c = *pIn_buf_cur++; \ - break; \ - } \ - } else { \ - c = 0; \ - break; \ - } \ - } \ - } else c = *pIn_buf_cur++; } MZ_MACRO_END - -#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) -#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END -#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END - -// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. -// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a -// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the -// bit buffer contains >=15 bits (deflate's max. Huffman code size). -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ - do { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ - if (temp >= 0) { \ - code_len = temp >> 9; \ - if ((code_len) && (num_bits >= code_len)) \ - break; \ - } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ - code_len = TINFL_FAST_LOOKUP_BITS; \ - do { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ - } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ - } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ - } while (num_bits < 15); - -// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read -// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully -// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. -// The slow path is only executed at the very end of the input buffer. -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ - int temp; mz_uint code_len, c; \ - if (num_bits < 15) { \ - if ((pIn_buf_end - pIn_buf_cur) < 2) { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ - } else { \ - bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ - } \ - } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ - code_len = temp >> 9, temp &= 511; \ - else { \ - code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ - } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END - -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) -{ - static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; - static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; - - tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; - const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; - size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; - - // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). - if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } - - num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; - TINFL_CR_BEGIN - - bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); - counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); - if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); - if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } - } - - do - { - TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; - if (r->m_type == 0) - { - TINFL_SKIP_BITS(5, num_bits & 7); - for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } - if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } - while ((counter) && (num_bits)) - { - TINFL_GET_BITS(51, dist, 8); - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)dist; - counter--; - } - while (counter) - { - size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } - while (pIn_buf_cur >= pIn_buf_end) - { - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) - { - TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); - } - else - { - TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); - } - } - n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); - TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; - } - } - else if (r->m_type == 3) - { - TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); - } - else - { - if (r->m_type == 1) - { - mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; - r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); - for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; - } - else - { - for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } - r->m_table_sizes[2] = 19; - } - for ( ; (int)r->m_type >= 0; r->m_type--) - { - int tree_next, tree_cur; tinfl_huff_table *pTable; - mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); - for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; - used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; - for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } - if ((65536 != total) && (used_syms > 1)) - { - TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); - } - for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) - { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; - cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); - if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } - rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); - for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) - { - tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; - } - tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; - } - if (r->m_type == 2) - { - for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) - { - mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } - if ((dist == 16) && (!counter)) - { - TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); - } - num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; - TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; - } - if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) - { - TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); - } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); - } - } - for ( ; ; ) - { - mz_uint8 *pSrc; - for ( ; ; ) - { - if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) - { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); - if (counter >= 256) - break; - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)counter; - } - else - { - int sym2; mz_uint code_len; -#if TINFL_USE_64BIT_BITBUF - if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } -#else - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - counter = sym2; bit_buf >>= code_len; num_bits -= code_len; - if (counter & 256) - break; - -#if !TINFL_USE_64BIT_BITBUF - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - bit_buf >>= code_len; num_bits -= code_len; - - pOut_buf_cur[0] = (mz_uint8)counter; - if (sym2 & 256) - { - pOut_buf_cur++; - counter = sym2; - break; - } - pOut_buf_cur[1] = (mz_uint8)sym2; - pOut_buf_cur += 2; - } - } - if ((counter &= 511) == 256) break; - - num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } - - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); - num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } - - dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; - if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - { - TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); - } - - pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); - - if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) - { - while (counter--) - { - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; - } - continue; - } -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - else if ((counter >= 9) && (counter <= dist)) - { - const mz_uint8 *pSrc_end = pSrc + (counter & ~7); - do - { - ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; - ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; - pOut_buf_cur += 8; - } while ((pSrc += 8) < pSrc_end); - if ((counter &= 7) < 3) - { - if (counter) - { - pOut_buf_cur[0] = pSrc[0]; - if (counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - continue; - } - } -#endif - do - { - pOut_buf_cur[0] = pSrc[0]; - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur[2] = pSrc[2]; - pOut_buf_cur += 3; pSrc += 3; - } while ((int)(counter -= 3) > 2); - if ((int)counter > 0) - { - pOut_buf_cur[0] = pSrc[0]; - if ((int)counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - } - } - } while (!(r->m_final & 1)); - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } - } - TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - TINFL_CR_FINISH - -common_exit: - r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; - *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; - if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) - { - const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; - mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; - while (buf_len) - { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) - { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; - } - return status; -} - -// Higher level helper functions. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; - *pOut_len = 0; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, - (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - src_buf_ofs += src_buf_size; - *pOut_len += dst_buf_size; - if (status == TINFL_STATUS_DONE) break; - new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; - pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); - if (!pNew_buf) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; - } - return pBuf; -} - -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); - status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; -} - -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - int result = 0; - tinfl_decompressor decomp; - mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; - if (!pDict) - return TINFL_STATUS_FAILED; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, - (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); - in_buf_ofs += in_buf_size; - if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) - break; - if (status != TINFL_STATUS_HAS_MORE_OUTPUT) - { - result = (status == TINFL_STATUS_DONE); - break; - } - dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); - } - MZ_FREE(pDict); - *pIn_buf_size = in_buf_ofs; - return result; -} - -#endif // #ifndef TINFL_HEADER_FILE_ONLY - -/* - This is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - For more information, please refer to -*/ diff --git a/Core/CppMicroServices/core/src/util/usAny.cpp b/Core/CppMicroServices/core/src/util/usAny.cpp index bac8be2580..723477b145 100644 --- a/Core/CppMicroServices/core/src/util/usAny.cpp +++ b/Core/CppMicroServices/core/src/util/usAny.cpp @@ -1,78 +1,46 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usAny.h" US_BEGIN_NAMESPACE -std::ostream& operator<< (std::ostream& os, const Any& any) +std::string any_value_to_string(const Any& any) { - return os << any.ToString(); + return any.ToString(); } -template -std::string container_to_string(Iterator i1, Iterator i2) +std::string any_value_to_json(const Any& val) { - std::stringstream ss; - ss << "("; - const Iterator begin = i1; - for ( ; i1 != i2; ++i1) - { - if (i1 == begin) ss << *i1; - else ss << "," << *i1; - } - ss << ")"; - return ss.str(); + return val.ToJSON(); } -std::string any_value_to_string(const std::vector& val) +std::string any_value_to_json(const std::string& val) { - return container_to_string(val.begin(), val.end()); + return '"' + val + '"'; } -std::string any_value_to_string(const std::list& val) +std::string any_value_to_json(bool val) { - return container_to_string(val.begin(), val.end()); -} - -std::string any_value_to_string(const std::vector& val) -{ - return container_to_string(val.begin(), val.end()); -} - -std::string any_value_to_string(const std::map& val) -{ - std::stringstream ss; - ss << "["; - typedef std::map::const_iterator Iterator; - Iterator i1 = val.begin(); - const Iterator begin = i1; - const Iterator end = val.end(); - for ( ; i1 != end; ++i1) - { - if (i1 == begin) ss << "\"" << i1->first << "\" => " << i1->second; - else ss << ", " << "\"" << i1->first << "\" => " << i1->second; - } - ss << "]"; - return ss.str(); + return val ? "true" : "false"; } US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/util/usLog_p.h b/Core/CppMicroServices/core/src/util/usLog_p.h index bd65a8f9dd..ff9304d0b9 100644 --- a/Core/CppMicroServices/core/src/util/usLog_p.h +++ b/Core/CppMicroServices/core/src/util/usLog_p.h @@ -1,99 +1,94 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USLOG_P_H #define USLOG_P_H #include +#include #include #include US_BEGIN_NAMESPACE US_Core_EXPORT void message_output(MsgType, const char* buf); struct LogMsg { - LogMsg(int t, const char* file, int ln, const char* func) - : type(static_cast(t)), enabled(true), buffer() + LogMsg(MsgType t, const char* file, int ln, const char* func) + : type(t), enabled(true), buffer() { buffer << "In " << func << " at " << file << ":" << ln << " : "; } + LogMsg() + : type(DebugMsg), enabled(false), buffer() + {} + + LogMsg(const LogMsg& other) + : type(other.type), enabled(other.enabled), buffer() + {} + ~LogMsg() { if(enabled) message_output(type, buffer.str().c_str()); } template LogMsg& operator<<(T t) { if (enabled) buffer << t; return *this; } LogMsg& operator()(bool flag) { this->enabled = flag; return *this; } private: MsgType type; bool enabled; std::stringstream buffer; }; -struct NoLogMsg { - - template - NoLogMsg& operator<<(T) - { - return *this; - } - - NoLogMsg& operator()(bool) - { - return *this; - } - -}; US_END_NAMESPACE #if defined(US_ENABLE_DEBUG_OUTPUT) - #define US_DEBUG US_PREPEND_NAMESPACE(LogMsg)(0, __FILE__, __LINE__, __FUNCTION__) + #define US_DEBUG (US_PREPEND_NAMESPACE(ModuleSettings)::GetLogLevel() > US_PREPEND_NAMESPACE(DebugMsg) ? US_PREPEND_NAMESPACE(LogMsg)() : US_PREPEND_NAMESPACE(LogMsg)(US_PREPEND_NAMESPACE(DebugMsg), __FILE__, __LINE__, __FUNCTION__)) #else - #define US_DEBUG true ? US_PREPEND_NAMESPACE(NoLogMsg)() : US_PREPEND_NAMESPACE(NoLogMsg)() + #define US_DEBUG true ? US_PREPEND_NAMESPACE(LogMsg)() : US_PREPEND_NAMESPACE(LogMsg)() #endif #if !defined(US_NO_INFO_OUTPUT) - #define US_INFO US_PREPEND_NAMESPACE(LogMsg)(1, __FILE__, __LINE__, __FUNCTION__) + #define US_INFO (US_PREPEND_NAMESPACE(ModuleSettings)::GetLogLevel() > US_PREPEND_NAMESPACE(InfoMsg) ? US_PREPEND_NAMESPACE(LogMsg)() : US_PREPEND_NAMESPACE(LogMsg)(US_PREPEND_NAMESPACE(InfoMsg), __FILE__, __LINE__, __FUNCTION__)) #else - #define US_INFO true ? US_PREPEND_NAMESPACE(NoLogMsg)() : US_PREPEND_NAMESPACE(NoLogMsg)() + #define US_INFO true ? US_PREPEND_NAMESPACE(LogMsg)() : US_PREPEND_NAMESPACE(LogMsg)() #endif #if !defined(US_NO_WARNING_OUTPUT) - #define US_WARN US_PREPEND_NAMESPACE(LogMsg)(2, __FILE__, __LINE__, __FUNCTION__) + #define US_WARN (US_PREPEND_NAMESPACE(ModuleSettings)::GetLogLevel() > US_PREPEND_NAMESPACE(WarningMsg) ? US_PREPEND_NAMESPACE(LogMsg)() : US_PREPEND_NAMESPACE(LogMsg)(US_PREPEND_NAMESPACE(WarningMsg), __FILE__, __LINE__, __FUNCTION__)) #else #define US_WARN true ? US_PREPEND_NAMESPACE(LogMsg)() : US_PREPEND_NAMESPACE(LogMsg)() #endif -#define US_ERROR US_PREPEND_NAMESPACE(LogMsg)(3, __FILE__, __LINE__, __FUNCTION__) +#define US_ERROR US_PREPEND_NAMESPACE(LogMsg)(US_PREPEND_NAMESPACE(ErrorMsg), __FILE__, __LINE__, __FUNCTION__) #endif // USLOG_P_H diff --git a/Core/CppMicroServices/core/src/util/usUncompressResourceData.c b/Core/CppMicroServices/core/src/util/usUncompressResourceData.c deleted file mode 100644 index 310491ed82..0000000000 --- a/Core/CppMicroServices/core/src/util/usUncompressResourceData.c +++ /dev/null @@ -1,59 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - - -#include "tinfl.c" - -#include - -static char us_uncompress_error_msg[256]; - -const char* us_uncompress_resource_error() -{ - return us_uncompress_error_msg; -} - -void us_uncompress_resource_data(const unsigned char* data, size_t size, - unsigned char* uncompressedData, size_t uncompressedSize) -{ - size_t bytesWritten = 0; - - memset(us_uncompress_error_msg, 0, sizeof(us_uncompress_error_msg)); - - if (data == NULL) - { - return; - } - - if (uncompressedData == NULL) - { - sprintf(us_uncompress_error_msg, "us_uncompress_resource_data: Buffer for uncomcpressing data is NULL"); - return; - } - - bytesWritten = tinfl_decompress_mem_to_mem(uncompressedData, uncompressedSize, - data, size, - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - if (bytesWritten != uncompressedSize) - { - sprintf(us_uncompress_error_msg, "us_uncompress_resource_data: tinfl_decompress_mem_to_mem failed"); - } -} diff --git a/Core/CppMicroServices/core/src/util/usUncompressResourceData.cpp b/Core/CppMicroServices/core/src/util/usUncompressResourceData.cpp deleted file mode 100644 index c7f9332ebf..0000000000 --- a/Core/CppMicroServices/core/src/util/usUncompressResourceData.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) German Cancer Research Center, - Division of Medical and Biological Informatics - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=============================================================================*/ - -#include "usUncompressResourceData.h" - -#ifdef US_ENABLE_RESOURCE_COMPRESSION -#include "usLog_p.h" - -extern "C" { -const char* us_uncompress_resource_error(); -void us_uncompress_resource_data(const unsigned char*, size_t, unsigned char*, size_t); -} - -US_BEGIN_NAMESPACE - -unsigned char* UncompressResourceData(const unsigned char* data, std::size_t size, - std::size_t* uncompressedSize) -{ - if (size <= 4) - { - if (size < 4 || (data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0)) - { - US_WARN << "UncompressResourceData: Input data is corrupted"; - return NULL; - } - } - std::size_t expectedSize = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - try - { - unsigned char* uncompressedData = new unsigned char[expectedSize]; - us_uncompress_resource_data(data+4, size-4, uncompressedData, expectedSize); - if (us_uncompress_resource_error()[0] != 0) - { - US_WARN << us_uncompress_resource_error(); - delete[] uncompressedData; - return NULL; - } - - if (uncompressedSize != NULL) - { - *uncompressedSize = expectedSize; - } - - return uncompressedData; - } - catch (const std::bad_alloc&) - { - US_WARN << "UncompressResourceData: Could not allocate enough memory for uncompressing resource data"; - return NULL; - } - - return NULL; -} - -US_END_NAMESPACE - -#else - -US_BEGIN_NAMESPACE - -unsigned char* UncompressResourceData(const unsigned char*, std::size_t, std::size_t*) -{ - return 0; -} - -US_END_NAMESPACE - -#endif // US_ENABLE_RESOURCE_COMPRESSION diff --git a/Core/CppMicroServices/core/src/util/usUtils.cpp b/Core/CppMicroServices/core/src/util/usUtils.cpp index 56cf918c31..2662b539d9 100644 --- a/Core/CppMicroServices/core/src/util/usUtils.cpp +++ b/Core/CppMicroServices/core/src/util/usUtils.cpp @@ -1,282 +1,350 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usUtils_p.h" #include "usLog_p.h" #include "usModuleInfo.h" #include "usModuleSettings.h" +#include #include #include #include +#include #ifdef US_PLATFORM_POSIX #include #include #include #include #else #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include - #include "dirent_win32_p.h" + #include "dirent_win32.h" #endif //------------------------------------------------------------------- // Module auto-loading //------------------------------------------------------------------- namespace { #if !defined(US_PLATFORM_LINUX) std::string library_suffix() { #ifdef US_PLATFORM_WINDOWS return ".dll"; #elif defined(US_PLATFORM_APPLE) return ".dylib"; #else return ".so"; #endif } #endif #ifdef US_PLATFORM_POSIX const char DIR_SEP = '/'; bool load_impl(const std::string& modulePath) { void* handle = dlopen(modulePath.c_str(), RTLD_NOW | RTLD_LOCAL); + if (handle == NULL) + { + US_WARN << dlerror(); + } return (handle != NULL); } #elif defined(US_PLATFORM_WINDOWS) const char DIR_SEP = '\\'; bool load_impl(const std::string& modulePath) { void* handle = LoadLibrary(modulePath.c_str()); + if (handle == NULL) + { + US_WARN << us::GetLastErrorStr(); + } return (handle != NULL); } #else #ifdef US_ENABLE_AUTOLOADING_SUPPORT #error "Missing load_impl implementation for this platform." #else bool load_impl(const std::string&) { return false; } #endif #endif } US_BEGIN_NAMESPACE -void AutoLoadModulesFromPath(const std::string& absoluteBasePath, const std::string& subDir) +std::vector AutoLoadModulesFromPath(const std::string& absoluteBasePath, const std::string& subDir) { + std::vector loadedModules; + std::string loadPath = absoluteBasePath + DIR_SEP + subDir; DIR* dir = opendir(loadPath.c_str()); #ifdef CMAKE_INTDIR // Try intermediate output directories if (dir == NULL) { std::size_t indexOfLastSeparator = absoluteBasePath.find_last_of(DIR_SEP); if (indexOfLastSeparator != std::string::npos) { std::string intermediateDir = absoluteBasePath.substr(indexOfLastSeparator+1); bool equalSubDir = intermediateDir.size() == std::strlen(CMAKE_INTDIR); for (std::size_t i = 0; equalSubDir && i < intermediateDir.size(); ++i) { if (std::tolower(intermediateDir[i]) != std::tolower(CMAKE_INTDIR[i])) { equalSubDir = false; } } if (equalSubDir) { loadPath = absoluteBasePath.substr(0, indexOfLastSeparator+1) + subDir + DIR_SEP + CMAKE_INTDIR; dir = opendir(loadPath.c_str()); } } } #endif if (dir != NULL) { struct dirent *ent = NULL; while ((ent = readdir(dir)) != NULL) { bool loadFile = true; #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN && ent->d_type != DT_REG) { loadFile = false; } #endif std::string entryFileName(ent->d_name); // On Linux, library file names can have version numbers appended. On other platforms, we // check the file ending. This could be refined for Linux in the future. #if !defined(US_PLATFORM_LINUX) if (entryFileName.rfind(library_suffix()) != (entryFileName.size() - library_suffix().size())) { loadFile = false; } #endif if (!loadFile) continue; std::string libPath = loadPath; if (!libPath.empty() && libPath.find_last_of(DIR_SEP) != libPath.size() -1) { libPath += DIR_SEP; } libPath += entryFileName; - US_INFO << "Auto-loading module " << libPath; + US_DEBUG << "Auto-loading module " << libPath; if (!load_impl(libPath)) { - US_WARN << "Auto-loading of module " << libPath << " failed: " << GetLastErrorStr(); + US_WARN << "Auto-loading of module " << libPath << " failed."; + } + else + { + loadedModules.push_back(libPath); } } closedir(dir); } + return loadedModules; } -void AutoLoadModules(const ModuleInfo& moduleInfo) +std::vector AutoLoadModules(const ModuleInfo& moduleInfo) { + std::vector loadedModules; + if (moduleInfo.autoLoadDir.empty()) { - return; + return loadedModules; } ModuleSettings::PathList autoLoadPaths = ModuleSettings::GetAutoLoadPaths(); std::size_t indexOfLastSeparator = moduleInfo.location.find_last_of(DIR_SEP); std::string moduleBasePath = moduleInfo.location.substr(0, indexOfLastSeparator); for (ModuleSettings::PathList::iterator i = autoLoadPaths.begin(); i != autoLoadPaths.end(); ++i) { if (*i == ModuleSettings::CURRENT_MODULE_PATH()) { // Load all modules from a directory located relative to this modules location // and named after this modules library name. *i = moduleBasePath; } } // We could have introduced a duplicate above, so remove it. std::sort(autoLoadPaths.begin(), autoLoadPaths.end()); autoLoadPaths.erase(std::unique(autoLoadPaths.begin(), autoLoadPaths.end()), autoLoadPaths.end()); for (ModuleSettings::PathList::iterator i = autoLoadPaths.begin(); i != autoLoadPaths.end(); ++i) { if (i->empty()) continue; - AutoLoadModulesFromPath(*i, moduleInfo.autoLoadDir); + std::vector paths = AutoLoadModulesFromPath(*i, moduleInfo.autoLoadDir); + loadedModules.insert(loadedModules.end(), paths.begin(), paths.end()); } + return loadedModules; } US_END_NAMESPACE //------------------------------------------------------------------- // Error handling //------------------------------------------------------------------- US_BEGIN_NAMESPACE std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } static MsgHandler handler = 0; MsgHandler installMsgHandler(MsgHandler h) { MsgHandler old = handler; handler = h; return old; } void message_output(MsgType msgType, const char *buf) { if (handler) { (*handler)(msgType, buf); } else { fprintf(stderr, "%s\n", buf); fflush(stderr); } if (msgType == ErrorMsg) { #if defined(_MSC_VER) && !defined(NDEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) // get the current report mode int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_ERROR, reportMode); int ret = _CrtDbgReport(_CRT_ERROR, __FILE__, __LINE__, CppMicroServices_VERSION_STR, buf); if (ret == 0 && reportMode & _CRTDBG_MODE_WNDW) return; // ignore else if (ret == 1) _CrtDbgBreak(); #endif #ifdef US_PLATFORM_POSIX abort(); // trap; generates core dump #else exit(1); // goodbye cruel world #endif } } +#ifdef US_HAVE_CXXABI_H +#include +#endif + +US_Core_EXPORT ::std::string GetDemangledName(const ::std::type_info& typeInfo) +{ + ::std::string result; +#ifdef US_HAVE_CXXABI_H + int status = 0; + char* demangled = abi::__cxa_demangle(typeInfo.name(), 0, 0, &status); + if (demangled && status == 0) + { + result = demangled; + free(demangled); + } +#elif defined(US_PLATFORM_WINDOWS) + const char* demangled = typeInfo.name(); + if (demangled != NULL) + { + result = demangled; + // remove "struct" qualifiers + std::size_t pos = 0; + while (pos != std::string::npos) + { + if ((pos = result.find("struct ", pos)) != std::string::npos) + { + result = result.substr(0, pos) + result.substr(pos + 7); + pos += 8; + } + } + // remove "class" qualifiers + pos = 0; + while (pos != std::string::npos) + { + if ((pos = result.find("class ", pos)) != std::string::npos) + { + result = result.substr(0, pos) + result.substr(pos + 6); + pos += 7; + } + } + } +#else + (void)typeInfo; +#endif + return result; +} + US_END_NAMESPACE diff --git a/Core/CppMicroServices/core/src/util/usUtils_p.h b/Core/CppMicroServices/core/src/util/usUtils_p.h index b097924d4d..778bca5291 100644 --- a/Core/CppMicroServices/core/src/util/usUtils_p.h +++ b/Core/CppMicroServices/core/src/util/usUtils_p.h @@ -1,52 +1,53 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USUTILS_H #define USUTILS_H #include #include +#include //------------------------------------------------------------------- // Module auto-loading //------------------------------------------------------------------- US_BEGIN_NAMESPACE struct ModuleInfo; -void AutoLoadModules(const ModuleInfo& moduleInfo); +std::vector AutoLoadModules(const ModuleInfo& moduleInfo); US_END_NAMESPACE //------------------------------------------------------------------- // Error handling //------------------------------------------------------------------- US_BEGIN_NAMESPACE US_Core_EXPORT std::string GetLastErrorStr(); US_END_NAMESPACE #endif // USUTILS_H diff --git a/Core/CppMicroServices/core/test/CMakeLists.txt b/Core/CppMicroServices/core/test/CMakeLists.txt index 04a58b2da7..3f8320b956 100644 --- a/Core/CppMicroServices/core/test/CMakeLists.txt +++ b/Core/CppMicroServices/core/test/CMakeLists.txt @@ -1,93 +1,100 @@ #----------------------------------------------------------------------------- # Configure files, include dirs, etc. #----------------------------------------------------------------------------- configure_file("${CMAKE_CURRENT_SOURCE_DIR}/usTestingConfig.h.in" "${PROJECT_BINARY_DIR}/include/usTestingConfig.h") include_directories(${CMAKE_CURRENT_SOURCE_DIR}) #----------------------------------------------------------------------------- # Create test modules #----------------------------------------------------------------------------- include(usFunctionCreateTestModule) set(_us_test_module_libs "" CACHE INTERNAL "" FORCE) add_subdirectory(modules) #----------------------------------------------------------------------------- # Add unit tests #----------------------------------------------------------------------------- set(_tests - usDebugOutputTest + usAnyTest usLDAPFilterTest + usLogTest + usModuleHooksTest + usModuleManifestTest usModuleTest usModuleResourceTest usServiceFactoryTest usServiceHooksTest usServiceRegistryPerformanceTest usServiceRegistryTest usServiceTemplateTest usServiceTrackerTest usStaticModuleResourceTest usStaticModuleTest ) if(US_BUILD_SHARED_LIBS) list(APPEND _tests - usModuleHooksTest - usModuleManifestTest usServiceListenerTest usSharedLibraryTest ) if(US_ENABLE_AUTOLOADING_SUPPORT) list(APPEND _tests usModuleAutoLoadTest) endif() endif() set(_additional_srcs + usTestDriverActivator.cpp usTestManager.cpp usTestUtilModuleListener.cpp ) set(_test_driver us${PROJECT_NAME}TestDriver) set(_test_sourcelist_extra_args ) create_test_sourcelist(_srcs ${_test_driver}.cpp ${_tests} ${_test_sourcelist_extra_args}) -usFunctionEmbedResources(_srcs EXECUTABLE_NAME ${_test_driver} FILES usTestResource.txt manifest.json) - # Generate a custom "module init" file for the test driver executable -if(US_BUILD_SHARED_LIBS) - usFunctionGenerateExecutableInit(_srcs IDENTIFIER ${_test_driver}) -endif() +usFunctionGenerateModuleInit(_srcs) -add_executable(${_test_driver} ${_srcs} ${_additional_srcs}) +add_executable(${_test_driver} ${_srcs} ${_additional_srcs} ${_test_driver}_resources.cpp) +set_property(TARGET ${_test_driver} APPEND PROPERTY COMPILE_DEFINITIONS US_MODULE_NAME=main) +set_property(TARGET ${_test_driver} PROPERTY US_MODULE_NAME main) if(NOT US_BUILD_SHARED_LIBS) + set_property(TARGET ${_test_driver} APPEND PROPERTY COMPILE_DEFINITIONS US_STATIC_MODULE) target_link_libraries(${_test_driver} ${_us_test_module_libs}) endif() + target_link_libraries(${_test_driver} ${Core_TARGET}) if(UNIX AND NOT APPLE) target_link_libraries(${_test_driver} rt) endif() +# Add resources +usFunctionAddResources(TARGET ${_test_driver} + FILES usTestResource.txt manifest.json + ZIP_ARCHIVES ${Core_TARGET} ${_us_test_module_libs}) + # Register tests foreach(_test ${_tests}) add_test(NAME ${_test} COMMAND ${_test_driver} ${_test}) endforeach() if(US_TEST_LABELS) set_tests_properties(${_tests} PROPERTIES LABELS "${US_TEST_LABELS}") endif() #----------------------------------------------------------------------------- # Add dependencies for shared libraries #----------------------------------------------------------------------------- if(US_BUILD_SHARED_LIBS) foreach(_test_module ${_us_test_module_libs}) add_dependencies(${_test_driver} ${_test_module}) endforeach() endif() diff --git a/Core/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp b/Core/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp index 11e145faec..ce8c625065 100644 --- a/Core/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp +++ b/Core/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp @@ -1,77 +1,77 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleAService.h" #include #include #include US_BEGIN_NAMESPACE struct TestModuleA : public TestModuleAService { TestModuleA(ModuleContext* mc) { US_INFO << "Registering TestModuleAService"; sr = mc->RegisterService(this); } void Unregister() { if (sr) { sr.Unregister(); sr = 0; } } private: ServiceRegistration sr; }; class TestModuleAActivator : public ModuleActivator { public: TestModuleAActivator() : s(0) {} ~TestModuleAActivator() { delete s; } void Load(ModuleContext* context) { s = new TestModuleA(context); } void Unload(ModuleContext*) { } private: TestModuleA* s; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleA, US_PREPEND_NAMESPACE(TestModuleAActivator)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleAActivator)) diff --git a/Core/CppMicroServices/core/test/modules/libA/usTestModuleAService.h b/Core/CppMicroServices/core/test/modules/libA/usTestModuleAService.h index 3ae29ec0bf..5aa89a6e6e 100644 --- a/Core/CppMicroServices/core/test/modules/libA/usTestModuleAService.h +++ b/Core/CppMicroServices/core/test/modules/libA/usTestModuleAService.h @@ -1,40 +1,38 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTMODULEASERVICE_H #define USTESTMODULEASERVICE_H #include #include US_BEGIN_NAMESPACE struct TestModuleAService { virtual ~TestModuleAService() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleAService), "org.cppmicroservices.TestModuleAService") - #endif // USTESTMODULEASERVICE_H diff --git a/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp b/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp index d2d03adeeb..dbc67185bb 100644 --- a/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp +++ b/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp @@ -1,76 +1,76 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleA2Service.h" #include #include US_BEGIN_NAMESPACE struct TestModuleA2 : public TestModuleA2Service { TestModuleA2(ModuleContext* mc) { US_INFO << "Registering TestModuleA2Service"; sr = mc->RegisterService(this); } void Unregister() { if (sr) { sr.Unregister(); } } private: ServiceRegistration sr; }; class TestModuleA2Activator : public ModuleActivator { public: TestModuleA2Activator() : s(0) {} ~TestModuleA2Activator() { delete s; } void Load(ModuleContext* context) { s = new TestModuleA2(context); } void Unload(ModuleContext* /*context*/) { s->Unregister(); } private: TestModuleA2* s; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleA2, US_PREPEND_NAMESPACE(TestModuleA2Activator)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleA2Activator)) diff --git a/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2Service.h b/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2Service.h index 3160ed106b..93be47ab88 100644 --- a/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2Service.h +++ b/Core/CppMicroServices/core/test/modules/libA2/usTestModuleA2Service.h @@ -1,40 +1,38 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTMODULEA2SERVICE_H #define USTESTMODULEA2SERVICE_H #include #include US_BEGIN_NAMESPACE struct TestModuleA2Service { virtual ~TestModuleA2Service() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleA2Service), "org.us.TestModuleA2Service") - #endif // USTESTMODULEA2SERVICE_H diff --git a/Core/CppMicroServices/core/test/modules/libBWithStatic/CMakeLists.txt b/Core/CppMicroServices/core/test/modules/libBWithStatic/CMakeLists.txt index 30b9cf7be6..80faf30ef9 100644 --- a/Core/CppMicroServices/core/test/modules/libBWithStatic/CMakeLists.txt +++ b/Core/CppMicroServices/core/test/modules/libBWithStatic/CMakeLists.txt @@ -1,12 +1,13 @@ -usFunctionCreateTestModuleWithResources(TestModuleB - SOURCES usTestModuleB.cpp - RESOURCES dynamic.txt res.txt) - set(BUILD_SHARED_LIBS 0) usFunctionCreateTestModuleWithResources(TestModuleImportedByB SOURCES usTestModuleImportedByB.cpp RESOURCES static.txt res.txt - RESOURCES_ROOT resources_static) + RESOURCES_ROOT resources_static + SKIP_MODULE_LIST) -target_link_libraries(TestModuleB TestModuleImportedByB) +set(BUILD_SHARED_LIBS ${US_BUILD_SHARED_LIBS}) +usFunctionCreateTestModuleWithResources(TestModuleB + SOURCES usTestModuleB.cpp + RESOURCES dynamic.txt res.txt + LINK_LIBRARIES TestModuleImportedByB) diff --git a/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp b/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp index cfe626f7d8..70b5befedf 100644 --- a/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp +++ b/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp @@ -1,70 +1,66 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleBService.h" #include #include #include US_BEGIN_NAMESPACE struct TestModuleB : public TestModuleBService { TestModuleB(ModuleContext* mc) { US_INFO << "Registering TestModuleBService"; mc->RegisterService(this); } }; class TestModuleBActivator : public ModuleActivator { public: TestModuleBActivator() : s(0) {} ~TestModuleBActivator() { delete s; } void Load(ModuleContext* context) { s = new TestModuleB(context); } void Unload(ModuleContext*) { } private: TestModuleB* s; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleB, US_PREPEND_NAMESPACE(TestModuleBActivator)) - +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleBActivator)) US_IMPORT_MODULE(TestModuleImportedByB) -US_IMPORT_MODULE_RESOURCES(TestModuleImportedByB) - -US_LOAD_IMPORTED_MODULES(TestModuleB, TestModuleImportedByB) diff --git a/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleBService.h b/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleBService.h index 044d0853e8..441ccfb14d 100644 --- a/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleBService.h +++ b/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleBService.h @@ -1,40 +1,38 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTMODULEBSERVICE_H #define USTESTMODULEBSERVICE_H #include #include US_BEGIN_NAMESPACE struct TestModuleBService { virtual ~TestModuleBService() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleBService), "org.cppmicroservices.TestModuleBService") - #endif // USTESTMODULEASERVICE_H diff --git a/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp b/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp index 30b5d331cb..b4a446fe03 100644 --- a/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp +++ b/Core/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp @@ -1,64 +1,64 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleBService.h" #include #include US_BEGIN_NAMESPACE struct TestModuleImportedByB : public TestModuleBService { TestModuleImportedByB(ModuleContext* mc) { US_INFO << "Registering TestModuleImportedByB"; mc->RegisterService(this); } }; class TestModuleImportedByBActivator : public ModuleActivator { public: TestModuleImportedByBActivator() : s(0) {} ~TestModuleImportedByBActivator() { delete s; } void Load(ModuleContext* context) { s = new TestModuleImportedByB(context); } void Unload(ModuleContext*) { } private: TestModuleImportedByB* s; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleImportedByB, US_PREPEND_NAMESPACE(TestModuleImportedByBActivator)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleImportedByBActivator)) diff --git a/Core/CppMicroServices/core/test/modules/libH/usTestModuleH.cpp b/Core/CppMicroServices/core/test/modules/libH/usTestModuleH.cpp index b004be226a..497b9b0025 100644 --- a/Core/CppMicroServices/core/test/modules/libH/usTestModuleH.cpp +++ b/Core/CppMicroServices/core/test/modules/libH/usTestModuleH.cpp @@ -1,147 +1,140 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include US_BEGIN_NAMESPACE struct TestModuleH { virtual ~TestModuleH() {} }; struct TestModuleH2 { virtual ~TestModuleH2() {} }; -US_END_NAMESPACE - -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleH), "org.cppmicroservices.TestModuleH") -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleH2), "org.cppmicroservices.TestModuleH2") - -US_BEGIN_NAMESPACE - class TestProduct : public TestModuleH { // Module* caller; public: TestProduct(Module* /*caller*/) //: caller(caller) {} }; class TestProduct2 : public TestProduct, public TestModuleH2 { public: TestProduct2(Module* caller) : TestProduct(caller) {} }; class TestModuleHPrototypeServiceFactory : public PrototypeServiceFactory { std::map > fcbind; // Map calling module with implementation public: InterfaceMap GetService(Module* caller, const ServiceRegistrationBase& /*sReg*/) { std::cout << "GetService (prototype) in H" << std::endl; TestProduct2* product = new TestProduct2(caller); fcbind[caller->GetModuleId()].push_back(product); return MakeInterfaceMap(product); } void UngetService(Module* caller, const ServiceRegistrationBase& /*sReg*/, const InterfaceMap& service) { TestProduct2* product = dynamic_cast(ExtractInterface(service)); delete product; fcbind[caller->GetModuleId()].remove(product); } }; class TestModuleHActivator : public ModuleActivator, public ServiceFactory { std::string thisServiceName; ServiceRegistration factoryService; ServiceRegistration prototypeFactoryService; ModuleContext* mc; std::map fcbind; // Map calling module with implementation TestModuleHPrototypeServiceFactory prototypeFactory; public: TestModuleHActivator() : thisServiceName(us_service_interface_iid()) , mc(NULL) {} void Load(ModuleContext* mc) { std::cout << "start in H" << std::endl; this->mc = mc; factoryService = mc->RegisterService(this); prototypeFactoryService = mc->RegisterService(static_cast(&prototypeFactory)); } void Unload(ModuleContext* /*mc*/) { factoryService.Unregister(); } InterfaceMap GetService(Module* caller, const ServiceRegistrationBase& /*sReg*/) { std::cout << "GetService in H" << std::endl; TestProduct* product = new TestProduct(caller); fcbind.insert(std::make_pair(caller->GetModuleId(), product)); return MakeInterfaceMap(product); } void UngetService(Module* caller, const ServiceRegistrationBase& /*sReg*/, const InterfaceMap& service) { TestModuleH* product = ExtractInterface(service); delete product; fcbind.erase(caller->GetModuleId()); } }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleH, us::TestModuleHActivator) +US_EXPORT_MODULE_ACTIVATOR(us::TestModuleHActivator) diff --git a/Core/CppMicroServices/core/test/modules/libM/usTestModuleM.cpp b/Core/CppMicroServices/core/test/modules/libM/usTestModuleM.cpp index 934056c84b..1a89029c2a 100644 --- a/Core/CppMicroServices/core/test/modules/libM/usTestModuleM.cpp +++ b/Core/CppMicroServices/core/test/modules/libM/usTestModuleM.cpp @@ -1,43 +1,43 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include US_BEGIN_NAMESPACE class TestModuleMActivator : public ModuleActivator { public: void Load(ModuleContext*) { } void Unload(ModuleContext*) { } }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleM, US_PREPEND_NAMESPACE(TestModuleMActivator)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleMActivator)) diff --git a/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt b/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt index e5ff2c8109..23f5a6b972 100644 --- a/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt +++ b/Core/CppMicroServices/core/test/modules/libRWithResources/CMakeLists.txt @@ -1,13 +1,23 @@ set(resource_files icons/compressable.bmp icons/cppmicroservices.png icons/readme.txt foo.txt special_chars.dummy.txt test.xml ) +configure_file(resources/foo.txt ${CMAKE_CURRENT_BINARY_DIR}/resources/foo2.txt @COPYONLY) + usFunctionCreateTestModuleWithResources(TestModuleR - SOURCES usTestModuleR.cpp - RESOURCES ${resource_files}) + SOURCES ${CMAKE_CURRENT_BINARY_DIR}/TestModuleR_binary_resources.cpp + RESOURCES ${resource_files} +) + +usFunctionAddResources( + TARGET TestModuleR + SOURCE_OUTPUT TestModuleR_binary_resources.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/resources + FILES foo2.txt +) diff --git a/Core/CppMicroServices/core/test/modules/libRWithResources/usTestModuleR.cpp b/Core/CppMicroServices/core/test/modules/libRWithResources/usTestModuleR.cpp index be8c0291ca..f2d811c366 100644 --- a/Core/CppMicroServices/core/test/modules/libRWithResources/usTestModuleR.cpp +++ b/Core/CppMicroServices/core/test/modules/libRWithResources/usTestModuleR.cpp @@ -1,43 +1,43 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include US_BEGIN_NAMESPACE class TestModuleRActivator : public ModuleActivator { public: void Load(ModuleContext*) { } void Unload(ModuleContext*) { } }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleR, US_PREPEND_NAMESPACE(TestModuleRActivator)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleRActivator)) diff --git a/Core/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp b/Core/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp index 8a4f7bc827..75cf59331c 100644 --- a/Core/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp +++ b/Core/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp @@ -1,142 +1,142 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "../../usServiceControlInterface.h" #include "usTestModuleSService0.h" #include "usTestModuleSService1.h" #include "usTestModuleSService2.h" #include "usTestModuleSService3.h" #include #include #include US_BEGIN_NAMESPACE class TestModuleS : public ServiceControlInterface, public TestModuleSService0, public TestModuleSService1, public TestModuleSService2, public TestModuleSService3 { public: TestModuleS(ModuleContext* mc) : mc(mc) { for(int i = 0; i <= 3; ++i) { servregs.push_back(ServiceRegistrationU()); } sreg = mc->RegisterService(this); sciReg = mc->RegisterService(this); } virtual const char* GetNameOfClass() const { return "TestModuleS"; } void ServiceControl(int offset, const std::string& operation, int ranking) { if (0 <= offset && offset <= 3) { if (operation == "register") { if (!servregs[offset]) { std::stringstream servicename; servicename << SERVICE << offset; InterfaceMap ifm; ifm.insert(std::make_pair(servicename.str(), static_cast(this))); ServiceProperties props; props.insert(std::make_pair(ServiceConstants::SERVICE_RANKING(), Any(ranking))); servregs[offset] = mc->RegisterService(ifm, props); } } if (operation == "unregister") { if (servregs[offset]) { ServiceRegistrationU sr1 = servregs[offset]; sr1.Unregister(); servregs[offset] = 0; } } } } void Unregister() { if (sreg) { sreg.Unregister(); } if (sciReg) { sciReg.Unregister(); } } private: - static const std::string SERVICE; // = "org.cppmicroservices.TestModuleSService" + static const std::string SERVICE; // = "us::TestModuleSService" ModuleContext* mc; std::vector servregs; ServiceRegistration sreg; ServiceRegistration sciReg; }; -const std::string TestModuleS::SERVICE = "org.cppmicroservices.TestModuleSService"; +const std::string TestModuleS::SERVICE = "us::TestModuleSService"; class TestModuleSActivator : public ModuleActivator { public: TestModuleSActivator() : s(0) {} ~TestModuleSActivator() { delete s; } void Load(ModuleContext* context) { s = new TestModuleS(context); } void Unload(ModuleContext* /*context*/) { #ifndef US_BUILD_SHARED_LIBS s->Unregister(); #endif } private: TestModuleS* s; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleS, US_PREPEND_NAMESPACE(TestModuleSActivator)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleSActivator)) diff --git a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService0.h b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService0.h index 5208c16775..82a0b7e22d 100644 --- a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService0.h +++ b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService0.h @@ -1,39 +1,37 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTMODULESSERVICE0_H #define USTESTMODULESSERVICE0_H #include US_BEGIN_NAMESPACE struct TestModuleSService0 { virtual ~TestModuleSService0() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleSService0), "org.cppmicroservices.TestModuleSService0") - #endif // USTESTMODULESSERVICE0_H diff --git a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService1.h b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService1.h index 2fadbe121c..84a56b7793 100644 --- a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService1.h +++ b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService1.h @@ -1,39 +1,37 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTMODULESSERVICE1_H #define USTESTMODULESSERVICE1_H #include US_BEGIN_NAMESPACE struct TestModuleSService1 { virtual ~TestModuleSService1() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleSService1), "org.cppmicroservices.TestModuleSService1") - #endif // USTESTMODULESSERVICE1_H diff --git a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService2.h b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService2.h index 901bd463a0..07e6633254 100644 --- a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService2.h +++ b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService2.h @@ -1,39 +1,37 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTMODULESSERVICE2_H #define USTESTMODULESSERVICE2_H #include US_BEGIN_NAMESPACE struct TestModuleSService2 { virtual ~TestModuleSService2() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleSService2), "org.cppmicroservices.TestModuleSService2") - #endif // USTESTMODULESSERVICE0_H diff --git a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService3.h b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService3.h index 57b0230e05..360e83d3a5 100644 --- a/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService3.h +++ b/Core/CppMicroServices/core/test/modules/libS/usTestModuleSService3.h @@ -1,39 +1,37 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTMODULESSERVICE3_H #define USTESTMODULESSERVICE3_H #include US_BEGIN_NAMESPACE struct TestModuleSService3 { virtual ~TestModuleSService3() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleSService3), "org.cppmicroservices.TestModuleSService3") - #endif // USTESTMODULESSERVICE0_H diff --git a/Core/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp b/Core/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp index 55dcc4dd46..7c1419babe 100644 --- a/Core/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp +++ b/Core/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp @@ -1,106 +1,106 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include "usFooService.h" US_BEGIN_NAMESPACE class ActivatorSL1 : public ModuleActivator, public ModulePropsInterface, public ServiceTrackerCustomizer { public: ActivatorSL1() : tracker(0), context(0) { } ~ActivatorSL1() { delete tracker; } void Load(ModuleContext* context) { this->context = context; InterfaceMap im = MakeInterfaceMap(this); im.insert(std::make_pair(std::string("ActivatorSL1"), this)); sr = context->RegisterService(im); delete tracker; tracker = new FooTracker(context, this); tracker->Open(); } void Unload(ModuleContext* /*context*/) { tracker->Close(); } const Properties& GetProperties() const { return props; } FooService* AddingService(const ServiceReferenceType& reference) { props["serviceAdded"] = true; FooService* fooService = context->GetService(reference); fooService->foo(); return fooService; } void ModifiedService(const ServiceReferenceType& /*reference*/, FooService* /*service*/) {} void RemovedService(const ServiceReferenceType& /*reference*/, FooService* /*service*/) { props["serviceRemoved"] = true; } private: ModulePropsInterface::Properties props; ServiceRegistrationU sr; typedef ServiceTracker FooTracker; FooTracker* tracker; ModuleContext* context; }; // ActivatorSL1 US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleSL1, US_PREPEND_NAMESPACE(ActivatorSL1)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(ActivatorSL1)) diff --git a/Core/CppMicroServices/core/test/modules/libSL1/usFooService.h b/Core/CppMicroServices/core/test/modules/libSL1/usFooService.h index 461cf12af0..8432d0de5e 100644 --- a/Core/CppMicroServices/core/test/modules/libSL1/usFooService.h +++ b/Core/CppMicroServices/core/test/modules/libSL1/usFooService.h @@ -1,40 +1,38 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USFOOSERVICE_H #define USFOOSERVICE_H #include US_BEGIN_NAMESPACE struct FooService { virtual ~FooService() {} virtual void foo() = 0; }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(FooService), "org.us.testing.FooService") - #endif // USFOOSERVICE_H diff --git a/Core/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp b/Core/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp index beae9c9362..17cb010005 100644 --- a/Core/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp +++ b/Core/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp @@ -1,99 +1,99 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include US_BEGIN_NAMESPACE class ActivatorSL3 : public ModuleActivator, public ModulePropsInterface, public ServiceTrackerCustomizer { public: ActivatorSL3() : tracker(0), context(0) {} ~ActivatorSL3() { delete tracker; } void Load(ModuleContext* context) { this->context = context; InterfaceMap im = MakeInterfaceMap(this); im.insert(std::make_pair(std::string("ActivatorSL3"), this)); sr = context->RegisterService(im); delete tracker; tracker = new FooTracker(context, this); tracker->Open(); } void Unload(ModuleContext* /*context*/) { tracker->Close(); } const ModulePropsInterface::Properties& GetProperties() const { return props; } FooService* AddingService(const ServiceReferenceType& reference) { props["serviceAdded"] = true; US_INFO << "SL3: Adding reference =" << reference; FooService* fooService = context->GetService(reference); fooService->foo(); return fooService; } void ModifiedService(const ServiceReferenceType& /*reference*/, FooService* /*service*/) { } void RemovedService(const ServiceReferenceType& reference, FooService* /*service*/) { props["serviceRemoved"] = true; US_INFO << "SL3: Removing reference =" << reference; } private: typedef ServiceTracker FooTracker; FooTracker* tracker; ModuleContext* context; ServiceRegistrationU sr; ModulePropsInterface::Properties props; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleSL3, US_PREPEND_NAMESPACE(ActivatorSL3)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(ActivatorSL3)) diff --git a/Core/CppMicroServices/core/test/modules/libSL4/usActivatorSL4.cpp b/Core/CppMicroServices/core/test/modules/libSL4/usActivatorSL4.cpp index cea41ce6fb..70d3952b7b 100644 --- a/Core/CppMicroServices/core/test/modules/libSL4/usActivatorSL4.cpp +++ b/Core/CppMicroServices/core/test/modules/libSL4/usActivatorSL4.cpp @@ -1,64 +1,64 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include US_BEGIN_NAMESPACE class ActivatorSL4 : public ModuleActivator, public FooService { public: ~ActivatorSL4() { } void foo() { US_INFO << "TestModuleSL4: Doing foo"; } void Load(ModuleContext* context) { sr = context->RegisterService(this); US_INFO << "TestModuleSL4: Registered " << sr; } void Unload(ModuleContext* /*context*/) { } private: ServiceRegistration sr; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleSL4, US_PREPEND_NAMESPACE(ActivatorSL4)) +US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(ActivatorSL4)) diff --git a/Core/CppMicroServices/core/test/usAnyTest.cpp b/Core/CppMicroServices/core/test/usAnyTest.cpp new file mode 100644 index 0000000000..8282484089 --- /dev/null +++ b/Core/CppMicroServices/core/test/usAnyTest.cpp @@ -0,0 +1,147 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include + +#include "usTestingMacros.h" + +#include + +US_USE_NAMESPACE + +int usAnyTest(int /*argc*/, char* /*argv*/[]) +{ + US_TEST_BEGIN("AnyTest"); + + Any anyBool = true; + US_TEST_CONDITION(anyBool.Type() == typeid(bool), "Any[bool].Type()") + US_TEST_CONDITION(any_cast(anyBool) == true, "any_cast()") + US_TEST_CONDITION(anyBool.ToString() == "1", "Any[bool].ToString()") + US_TEST_CONDITION(anyBool.ToJSON() == "true", "Any[bool].ToJSON()") + anyBool = false; + US_TEST_CONDITION(anyBool.ToString() == "0", "Any[bool].ToString()") + US_TEST_CONDITION(anyBool.ToJSON() == "false", "Any[bool].ToJSON()") + + Any anyInt = 13; + US_TEST_CONDITION(anyInt.Type() == typeid(int), "Any[int].Type()") + US_TEST_CONDITION(any_cast(anyInt) == 13, "any_cast()") + US_TEST_CONDITION(anyInt.ToString() == "13", "Any[int].ToString()") + US_TEST_CONDITION(anyInt.ToJSON() == "13", "Any[int].ToJSON()") + + Any anyChar = 'a'; + US_TEST_CONDITION(anyChar.Type() == typeid(char), "Any[char].Type()") + US_TEST_CONDITION(any_cast(anyChar) == 'a', "any_cast()") + US_TEST_CONDITION(anyChar.ToString() == "a", "Any[char].ToString()") + US_TEST_CONDITION(anyChar.ToJSON() == "a", "Any[char].ToJSON()") + + Any anyFloat = 0.2f; + US_TEST_CONDITION(anyFloat.Type() == typeid(float), "Any[float].Type()") + US_TEST_CONDITION(any_cast(anyFloat) - 0.2f < std::numeric_limits::epsilon(), "any_cast()") + US_TEST_CONDITION(anyFloat.ToString() == "0.2", "Any[float].ToString()") + US_TEST_CONDITION(anyFloat.ToString() == "0.2", "Any[float].ToJSON()") + + Any anyDouble = 0.5; + US_TEST_CONDITION(anyDouble.Type() == typeid(double), "Any[double].Type()") + US_TEST_CONDITION(any_cast(anyDouble) - 0.5 < std::numeric_limits::epsilon(), "any_cast()") + US_TEST_CONDITION(anyDouble.ToString() == "0.5", "Any[double].ToString()") + US_TEST_CONDITION(anyDouble.ToString() == "0.5", "Any[double].ToJSON()") + + Any anyString = std::string("hello"); + US_TEST_CONDITION(anyString.Type() == typeid(std::string), "Any[std::string].Type()") + US_TEST_CONDITION(any_cast(anyString) == "hello", "any_cast()") + US_TEST_CONDITION(anyString.ToString() == "hello", "Any[std::string].ToString()") + US_TEST_CONDITION(anyString.ToJSON() == "\"hello\"", "Any[std::string].ToJSON()") + + std::vector vecInts; + vecInts.push_back(1); + vecInts.push_back(2); + Any anyVectorOfInts = vecInts; + US_TEST_CONDITION(anyVectorOfInts.Type() == typeid(std::vector), "Any[std::vector].Type()") + US_TEST_CONDITION(any_cast >(anyVectorOfInts) == vecInts, "any_cast>()") + US_TEST_CONDITION(anyVectorOfInts.ToString() == "[1,2]", "Any[std::vector].ToString()") + US_TEST_CONDITION(anyVectorOfInts.ToJSON() == "[1,2]", "Any[std::vector].ToJSON()") + + std::list listInts; + listInts.push_back(1); + listInts.push_back(2); + Any anyListOfInts = listInts; + US_TEST_CONDITION(anyListOfInts.Type() == typeid(std::list), "Any[std::list].Type()") + US_TEST_CONDITION(any_cast >(anyListOfInts) == listInts, "any_cast>()") + US_TEST_CONDITION(anyListOfInts.ToString() == "[1,2]", "Any[std::list].ToString()") + US_TEST_CONDITION(anyListOfInts.ToJSON() == "[1,2]", "Any[std::list].ToJSON()") + + std::set setInts; + setInts.insert(1); + setInts.insert(2); + Any anySetOfInts = setInts; + US_TEST_CONDITION(anySetOfInts.Type() == typeid(std::set), "Any[std::set].Type()") + US_TEST_CONDITION(any_cast >(anySetOfInts) == setInts, "any_cast>()") + US_TEST_CONDITION(anySetOfInts.ToString() == "[1,2]", "Any[std::set].ToString()") + US_TEST_CONDITION(anySetOfInts.ToJSON() == "[1,2]", "Any[std::set].ToJSON()") + + std::vector vecAny; + vecAny.push_back(1); + vecAny.push_back(std::string("hello")); + Any anyVectorOfAnys = vecAny; + US_TEST_CONDITION(anyVectorOfAnys.Type() == typeid(std::vector), "Any[std::vector].Type()") + US_TEST_CONDITION(anyVectorOfAnys.ToString() == "[1,hello]", "Any[std::vector].ToString()") + US_TEST_CONDITION(anyVectorOfAnys.ToJSON() == "[1,\"hello\"]", "Any[std::vector].ToJSON()") + + std::list listAny; + listAny.push_back(1); + listAny.push_back(std::string("hello")); + Any anyListOfAnys = listAny; + US_TEST_CONDITION(anyListOfAnys.Type() == typeid(std::list), "Any[std::list].Type()") + US_TEST_CONDITION(anyListOfAnys.ToString() == "[1,hello]", "Any[std::list].ToString()") + US_TEST_CONDITION(anyListOfAnys.ToJSON() == "[1,\"hello\"]", "Any[std::list].ToJSON()") + + std::map map1; + map1["one"] = 1; + map1["two"] = 2; + Any anyMap1 = map1; + US_TEST_CONDITION(anyMap1.Type() == typeid(std::map), "Any[std::map].Type()") + US_TEST_CONDITION((any_cast >(anyMap1) == map1), "any_cast>()") + US_TEST_CONDITION(anyMap1.ToString() == "{one : 1, two : 2}", "Any[std::map].ToString()") + US_TEST_CONDITION(anyMap1.ToJSON() == "{\"one\" : 1, \"two\" : 2}", "Any[std::map].ToJSON()") + + std::map map2; + map2[1] = 0.3; + map2[3] = std::string("bye"); + Any anyMap2 = map2; + US_TEST_CONDITION(anyMap2.Type() == typeid(std::map), "Any[std::map].Type()") + US_TEST_CONDITION(anyMap2.ToString() == "{1 : 0.3, 3 : bye}", "Any[std::map].ToString()") + US_TEST_CONDITION(anyMap2.ToJSON() == "{\"1\" : 0.3, \"3\" : \"bye\"}", "Any[std::map].ToJSON()") + + std::map map3; + map3["number"] = 5; + std::vector numbers; + numbers.push_back(9); + numbers.push_back(8); + numbers.push_back(7); + map3["vector"] = numbers; + map3["map"] = map2; + Any anyMap3 = map3; + US_TEST_CONDITION(anyMap3.Type() == typeid(std::map), "Any[std::map].Type()") + US_TEST_CONDITION(anyMap3.ToString() == "{map : {1 : 0.3, 3 : bye}, number : 5, vector : [9,8,7]}", "Any[std::map].ToString()") + US_TEST_CONDITION(anyMap3.ToJSON() == "{\"map\" : {\"1\" : 0.3, \"3\" : \"bye\"}, \"number\" : 5, \"vector\" : [9,8,7]}", "Any[std::map].ToJSON()") + + US_TEST_END() +} diff --git a/Core/CppMicroServices/core/test/usDebugOutputTest.cpp b/Core/CppMicroServices/core/test/usLogTest.cpp similarity index 61% rename from Core/CppMicroServices/core/test/usDebugOutputTest.cpp rename to Core/CppMicroServices/core/test/usLogTest.cpp index ae87a6eb32..13839cdf45 100644 --- a/Core/CppMicroServices/core/test/usDebugOutputTest.cpp +++ b/Core/CppMicroServices/core/test/usLogTest.cpp @@ -1,99 +1,177 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include "usTestingMacros.h" US_USE_NAMESPACE static int lastMsgType = -1; static std::string lastMsg; void handleMessages(MsgType type, const char* msg) { lastMsgType = type; lastMsg.assign(msg); } void resetLastMsg() { lastMsgType = -1; lastMsg.clear(); } -int usDebugOutputTest(int /*argc*/, char* /*argv*/[]) +void logMsg(MsgType msgType, int logLevel) { - US_TEST_BEGIN("DebugOutputTest"); + resetLastMsg(); + std::string logMsg; + switch (msgType) + { + case DebugMsg: + { + logMsg = "Debug msg"; + US_DEBUG << logMsg; +#if !defined(US_ENABLE_DEBUG_OUTPUT) + if (logLevel == DebugMsg) logLevel = InfoMsg; +#endif + break; + } + case InfoMsg: + { + logMsg = "Info msg"; + US_INFO << logMsg; + break; + } + case WarningMsg: + { + logMsg = "Warning msg"; + US_WARN << logMsg; + break; + } + case ErrorMsg: + { + // Skip error messages + logLevel = 100; + break; + } + } + + if (msgType >= logLevel) + { + US_TEST_CONDITION(lastMsgType == msgType && lastMsg.find(logMsg) != std::string::npos, "Testing for logged message") + } + else + { + US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing for skipped log message") + } +} +void testLogMessages() +{ // Use the default message handler + installMsgHandler(0); { US_DEBUG << "Msg"; US_DEBUG(false) << "Msg"; US_INFO << "Msg"; US_INFO(false) << "Msg"; US_WARN << "Msg"; US_WARN(false) << "Msg"; } US_TEST_CONDITION(lastMsg.empty(), "Testing default message handler"); resetLastMsg(); installMsgHandler(handleMessages); { US_DEBUG << "Msg"; } #if !defined(US_ENABLE_DEBUG_OUTPUT) US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing suppressed debug message") #else US_TEST_CONDITION(lastMsgType == 0 && lastMsg.find("Msg") != std::string::npos, "Testing debug message") #endif resetLastMsg(); { US_DEBUG(false) << "No msg"; } US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing disabled debug message") resetLastMsg(); { US_INFO << "Info msg"; } US_TEST_CONDITION(lastMsgType == 1 && lastMsg.find("Info msg") != std::string::npos, "Testing informational message") resetLastMsg(); { US_WARN << "Warn msg"; } US_TEST_CONDITION(lastMsgType == 2 && lastMsg.find("Warn msg") != std::string::npos, "Testing warning message") resetLastMsg(); // We cannot test US_ERROR since it will call abort(). installMsgHandler(0); { US_INFO << "Info msg"; } US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing message handler reset") + resetLastMsg(); +} + +void testLogLevels() +{ + installMsgHandler(handleMessages); + + MsgType logLevel = ModuleSettings::GetLogLevel(); + US_TEST_CONDITION_REQUIRED(logLevel == DebugMsg, "Default log level") + + logMsg(DebugMsg, logLevel); + logMsg(InfoMsg, logLevel); + logMsg(WarningMsg, logLevel); + logMsg(ErrorMsg, logLevel); + + for (int level = ErrorMsg; level >= 0; --level) + { + ModuleSettings::SetLogLevel(static_cast(level)); + logMsg(DebugMsg, level); + logMsg(InfoMsg, level); + logMsg(WarningMsg, level); + logMsg(ErrorMsg, level); + } + + installMsgHandler(0); + resetLastMsg(); +} + +int usLogTest(int /*argc*/, char* /*argv*/[]) +{ + US_TEST_BEGIN("DebugOutputTest"); + + testLogMessages(); + testLogLevels(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usModuleAutoLoadTest.cpp b/Core/CppMicroServices/core/test/usModuleAutoLoadTest.cpp index db5dae1a76..08b09d0215 100644 --- a/Core/CppMicroServices/core/test/usModuleAutoLoadTest.cpp +++ b/Core/CppMicroServices/core/test/usModuleAutoLoadTest.cpp @@ -1,176 +1,175 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include "usTestUtilModuleListener.h" #include "usTestingMacros.h" -#include +#include US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif void testDefaultAutoLoadPath(bool autoLoadEnabled) { ModuleContext* mc = GetModuleContext(); assert(mc); TestModuleListener listener; - try - { - mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged); - } - catch (const std::logic_error& ise) - { - US_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); - throw; - } + ModuleListenerRegistrationHelper listenerReg(mc, &listener, &TestModuleListener::ModuleChanged); SharedLibrary libAL(LIB_PATH, "TestModuleAL"); try { libAL.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } - Module* moduleAL = ModuleRegistry::GetModule("TestModuleAL Module"); + Module* moduleAL = ModuleRegistry::GetModule("TestModuleAL"); US_TEST_CONDITION_REQUIRED(moduleAL != NULL, "Test for existing module TestModuleAL") - US_TEST_CONDITION(moduleAL->GetName() == "TestModuleAL Module", "Test module name") + US_TEST_CONDITION(moduleAL->GetName() == "TestModuleAL", "Test module name") // check the listeners for events std::vector pEvts; pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleAL)); - Module* moduleAL_1 = ModuleRegistry::GetModule("TestModuleAL_1 Module"); + Any loadedModules = moduleAL->GetProperty(Module::PROP_AUTOLOADED_MODULES()); + Module* moduleAL_1 = ModuleRegistry::GetModule("TestModuleAL_1"); if (autoLoadEnabled) { US_TEST_CONDITION_REQUIRED(moduleAL_1 != NULL, "Test for existing auto-loaded module TestModuleAL_1") - US_TEST_CONDITION(moduleAL_1->GetName() == "TestModuleAL_1 Module", "Test module name") + US_TEST_CONDITION(moduleAL_1->GetName() == "TestModuleAL_1", "Test module name") + US_TEST_CONDITION_REQUIRED(!loadedModules.Empty(), "Test for PROP_AUTOLOADED_MODULES property") + US_TEST_CONDITION_REQUIRED(loadedModules.Type() == typeid(std::vector), "Test for PROP_AUTOLOADED_MODULES property type") + std::vector loadedModulesVec = any_cast >(loadedModules); + US_TEST_CONDITION_REQUIRED(loadedModulesVec.size() == 1, "Test for PROP_AUTOLOADED_MODULES vector size") + US_TEST_CONDITION_REQUIRED(loadedModulesVec[0] == moduleAL_1->GetLocation(), "Test for PROP_AUTOLOADED_MODULES vector content") pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleAL_1)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleAL_1)); } else { US_TEST_CONDITION_REQUIRED(moduleAL_1 == NULL, "Test for non-existing auto-loaded module TestModuleAL_1") + US_TEST_CONDITION_REQUIRED(loadedModules.Empty(), "Test for empty PROP_AUTOLOADED_MODULES property") } pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleAL)); US_TEST_CONDITION(listener.CheckListenerEvents(pEvts), "Test for unexpected events"); mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged); libAL.Unload(); } void testCustomAutoLoadPath() { ModuleContext* mc = GetModuleContext(); assert(mc); TestModuleListener listener; try { mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); throw; } SharedLibrary libAL2(LIB_PATH, "TestModuleAL2"); try { libAL2.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } - Module* moduleAL2 = ModuleRegistry::GetModule("TestModuleAL2 Module"); + Module* moduleAL2 = ModuleRegistry::GetModule("TestModuleAL2"); US_TEST_CONDITION_REQUIRED(moduleAL2 != NULL, "Test for existing module TestModuleAL2") - US_TEST_CONDITION(moduleAL2->GetName() == "TestModuleAL2 Module", "Test module name") + US_TEST_CONDITION(moduleAL2->GetName() == "TestModuleAL2", "Test module name") // check the listeners for events std::vector pEvts; pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleAL2)); - Module* moduleAL2_1 = ModuleRegistry::GetModule("TestModuleAL2_1 Module"); + Module* moduleAL2_1 = ModuleRegistry::GetModule("TestModuleAL2_1"); #ifdef US_ENABLE_AUTOLOADING_SUPPORT US_TEST_CONDITION_REQUIRED(moduleAL2_1 != NULL, "Test for existing auto-loaded module TestModuleAL2_1") - US_TEST_CONDITION(moduleAL2_1->GetName() == "TestModuleAL2_1 Module", "Test module name") + US_TEST_CONDITION(moduleAL2_1->GetName() == "TestModuleAL2_1", "Test module name") pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleAL2_1)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleAL2_1)); #else US_TEST_CONDITION_REQUIRED(moduleAL2_1 == NULL, "Test for non-existing aut-loaded module TestModuleAL2_1") #endif pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleAL2)); US_TEST_CONDITION(listener.CheckListenerEvents(pEvts), "Test for unexpected events"); mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged); } } // end unnamed namespace int usModuleAutoLoadTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleLoaderTest"); ModuleSettings::SetAutoLoadingEnabled(false); testDefaultAutoLoadPath(false); ModuleSettings::SetAutoLoadingEnabled(true); testDefaultAutoLoadPath(true); testCustomAutoLoadPath(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usModuleHooksTest.cpp b/Core/CppMicroServices/core/test/usModuleHooksTest.cpp index b1dea8ccdc..b9c68bdf56 100644 --- a/Core/CppMicroServices/core/test/usModuleHooksTest.cpp +++ b/Core/CppMicroServices/core/test/usModuleHooksTest.cpp @@ -1,187 +1,194 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include -#include #include #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif class TestModuleListener { public: void ModuleChanged(const ModuleEvent moduleEvent) { this->events.push_back(moduleEvent); } std::vector events; }; class TestModuleFindHook : public ModuleFindHook { public: void Find(const ModuleContext* /*context*/, ShrinkableVector& modules) { for (ShrinkableVector::iterator i = modules.begin(); i != modules.end();) { - if ((*i)->GetName() == "TestModuleA Module") + if ((*i)->GetName() == "TestModuleA") { i = modules.erase(i); } else { ++i; } } } }; class TestModuleEventHook : public ModuleEventHook { public: void Event(const ModuleEvent& event, ShrinkableVector& contexts) { if (event.GetType() == ModuleEvent::LOADING || event.GetType() == ModuleEvent::UNLOADING) { contexts.erase(std::remove(contexts.begin(), contexts.end(), GetModuleContext()), contexts.end()); } } }; void TestFindHook() { SharedLibrary libA(LIB_PATH, "TestModuleA"); + +#ifdef US_BUILD_SHARED_LIBS try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } +#endif - Module* moduleA = GetModuleContext()->GetModule("TestModuleA Module"); + Module* moduleA = GetModuleContext()->GetModule("TestModuleA"); US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") - US_TEST_CONDITION(moduleA->GetName() == "TestModuleA Module", "Test module name") + US_TEST_CONDITION(moduleA->GetName() == "TestModuleA", "Test module name") US_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly"); long moduleAId = moduleA->GetModuleId(); US_TEST_CONDITION_REQUIRED(moduleAId > 0, "Test for valid module id") US_TEST_CONDITION_REQUIRED(GetModuleContext()->GetModule(moduleAId) != NULL, "Test for non-filtered GetModule(long) result") TestModuleFindHook findHook; ServiceRegistration findHookReg = GetModuleContext()->RegisterService(&findHook); US_TEST_CONDITION_REQUIRED(GetModuleContext()->GetModule(moduleAId) == NULL, "Test for filtered GetModule(long) result") std::vector modules = GetModuleContext()->GetModules(); for (std::vector::iterator i = modules.begin(); i != modules.end(); ++i) { - if((*i)->GetName() == "TestModuleA Module") + if((*i)->GetName() == "TestModuleA") { US_TEST_FAILED_MSG(<< "TestModuleA not filtered from GetModules()") } } findHookReg.Unregister(); libA.Unload(); } +#ifdef US_BUILD_SHARED_LIBS void TestEventHook() { TestModuleListener moduleListener; GetModuleContext()->AddModuleListener(&moduleListener, &TestModuleListener::ModuleChanged); SharedLibrary libA(LIB_PATH, "TestModuleA"); try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 2, "Test for received load module events") libA.Unload(); US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 4, "Test for received unload module events") TestModuleEventHook eventHook; ServiceRegistration eventHookReg = GetModuleContext()->RegisterService(&eventHook); moduleListener.events.clear(); try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 1, "Test for filtered load module events") US_TEST_CONDITION_REQUIRED(moduleListener.events[0].GetType() == ModuleEvent::LOADED, "Test for LOADED event") libA.Unload(); US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 2, "Test for filtered unload module events") US_TEST_CONDITION_REQUIRED(moduleListener.events[1].GetType() == ModuleEvent::UNLOADED, "Test for UNLOADED event") eventHookReg.Unregister(); GetModuleContext()->RemoveModuleListener(&moduleListener, &TestModuleListener::ModuleChanged); } +#endif } // end unnamed namespace int usModuleHooksTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleHooksTest"); TestFindHook(); + +#ifdef US_BUILD_SHARED_LIBS TestEventHook(); +#endif US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usModuleManifestTest.cpp b/Core/CppMicroServices/core/test/usModuleManifestTest.cpp index 757a38da59..62f7eb0388 100644 --- a/Core/CppMicroServices/core/test/usModuleManifestTest.cpp +++ b/Core/CppMicroServices/core/test/usModuleManifestTest.cpp @@ -1,96 +1,96 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include -#include #include #include #include #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif } // end unnamed namespace int usModuleManifestTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleManifestTest"); SharedLibrary target(LIB_PATH, "TestModuleM"); - // Start the test target +#ifdef US_BUILD_SHARED_LIBS try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() << " + in frameSL02a:FAIL" ); } +#endif - Module* moduleM = ModuleRegistry::GetModule("TestModuleM Module"); + Module* moduleM = ModuleRegistry::GetModule("TestModuleM"); US_TEST_CONDITION_REQUIRED(moduleM != 0, "Test for existing module TestModuleM") - US_TEST_CONDITION(moduleM->GetProperty(Module::PROP_NAME()).ToString() == "TestModuleM Module", "Module name") - US_TEST_CONDITION(moduleM->GetName() == "TestModuleM Module", "Module name 2") + US_TEST_CONDITION(moduleM->GetProperty(Module::PROP_NAME()).ToString() == "TestModuleM", "Module name") + US_TEST_CONDITION(moduleM->GetName() == "TestModuleM", "Module name 2") US_TEST_CONDITION(moduleM->GetProperty(Module::PROP_DESCRIPTION()).ToString() == "My Module description", "Module description") US_TEST_CONDITION(moduleM->GetLocation() == moduleM->GetProperty(Module::PROP_LOCATION()).ToString(), "Module location") US_TEST_CONDITION(moduleM->GetProperty(Module::PROP_VERSION()).ToString() == "1.0.0", "Module version") US_TEST_CONDITION(moduleM->GetVersion() == ModuleVersion(1,0,0), "Module version 2") Any anyVector = moduleM->GetProperty("vector"); US_TEST_CONDITION_REQUIRED(anyVector.Type() == typeid(std::vector), "vector type") std::vector& vec = ref_any_cast >(anyVector); US_TEST_CONDITION_REQUIRED(vec.size() == 3, "vector size") US_TEST_CONDITION_REQUIRED(vec[0].Type() == typeid(std::string), "vector 0 type") US_TEST_CONDITION_REQUIRED(vec[0].ToString() == "first", "vector 0 value") US_TEST_CONDITION_REQUIRED(vec[1].Type() == typeid(int), "vector 1 type") US_TEST_CONDITION_REQUIRED(any_cast(vec[1]) == 2, "vector 1 value") Any anyMap = moduleM->GetProperty("map"); US_TEST_CONDITION_REQUIRED(anyMap.Type() == typeid(std::map), "map type") std::map& m = ref_any_cast >(anyMap); US_TEST_CONDITION_REQUIRED(m.size() == 3, "map size") US_TEST_CONDITION_REQUIRED(m["string"].Type() == typeid(std::string), "map 0 type") US_TEST_CONDITION_REQUIRED(m["string"].ToString() == "hi", "map 0 value") US_TEST_CONDITION_REQUIRED(m["number"].Type() == typeid(int), "map 1 type") US_TEST_CONDITION_REQUIRED(any_cast(m["number"]) == 4, "map 1 value") US_TEST_CONDITION_REQUIRED(m["list"].Type() == typeid(std::vector), "map 2 type") US_TEST_CONDITION_REQUIRED(any_cast >(m["list"]).size() == 2, "map 2 value size") target.Unload(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usModulePropsInterface.h b/Core/CppMicroServices/core/test/usModulePropsInterface.h index 27bd6e7568..d5254ecf09 100644 --- a/Core/CppMicroServices/core/test/usModulePropsInterface.h +++ b/Core/CppMicroServices/core/test/usModulePropsInterface.h @@ -1,44 +1,42 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USMODULEPROPSINTERFACE_H #define USMODULEPROPSINTERFACE_H #include #include US_BEGIN_NAMESPACE struct ModulePropsInterface { typedef ServiceProperties Properties; virtual ~ModulePropsInterface() {} virtual const Properties& GetProperties() const = 0; }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(ModulePropsInterface), "org.us.testing.ModulePropsInterface") - #endif // USMODULEPROPSINTERFACE_H diff --git a/Core/CppMicroServices/core/test/usModuleResourceTest.cpp b/Core/CppMicroServices/core/test/usModuleResourceTest.cpp index c27514058f..507a1e5348 100644 --- a/Core/CppMicroServices/core/test/usModuleResourceTest.cpp +++ b/Core/CppMicroServices/core/test/usModuleResourceTest.cpp @@ -1,545 +1,455 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include "usTestingMacros.h" -#include +#include #include US_USE_NAMESPACE namespace { // Please confirm that a character count differing from the following targets is not due to // a misconfiguration of your versioning software (Correct line endings for your system) // See issue #18 ( https://github.com/saschazelzer/CppMicroServices/issues/18 ) void checkResourceInfo(const ModuleResource& res, const std::string& path, const std::string& baseName, const std::string& completeBaseName, const std::string& suffix, const std::string& completeSuffix, - int size, bool children = false, bool compressed = false) + int size, bool children = false) { US_TEST_CONDITION_REQUIRED(res.IsValid(), "Valid resource") US_TEST_CONDITION(res.GetBaseName() == baseName, "GetBaseName()") US_TEST_CONDITION(res.GetChildren().empty() == !children, "No children") US_TEST_CONDITION(res.GetCompleteBaseName() == completeBaseName, "GetCompleteBaseName()") US_TEST_CONDITION(res.GetName() == completeBaseName + "." + suffix, "GetName()") US_TEST_CONDITION(res.GetResourcePath() == path + completeBaseName + "." + suffix, "GetResourcePath()") US_TEST_CONDITION(res.GetPath() == path, "GetPath()") US_TEST_CONDITION(res.GetSize() == size, "Data size") US_TEST_CONDITION(res.GetSuffix() == suffix, "Suffix") US_TEST_CONDITION(res.GetCompleteSuffix() == completeSuffix, "Complete suffix") - US_TEST_CONDITION(res.IsCompressed() == compressed, "Compression flag") } void testTextResource(Module* module) { ModuleResource res = module->GetResource("foo.txt"); #ifdef US_PLATFORM_WINDOWS checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 16, false); const std::streampos ssize(13); const std::string fileData = "foo and\nbar\n\n"; #else checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 13, false); const std::streampos ssize(12); const std::string fileData = "foo and\nbar\n"; #endif ModuleResourceStream rs(res); rs.seekg(0, std::ios::end); US_TEST_CONDITION(rs.tellg() == ssize, "Stream content length"); rs.seekg(0, std::ios::beg); std::string content; content.reserve(res.GetSize()); char buffer[1024]; while (rs.read(buffer, sizeof(buffer))) { content.append(buffer, sizeof(buffer)); } content.append(buffer, static_cast(rs.gcount())); US_TEST_CONDITION(rs.eof(), "EOF check"); US_TEST_CONDITION(content == fileData, "Resource content"); rs.clear(); rs.seekg(0); US_TEST_CONDITION_REQUIRED(rs.tellg() == std::streampos(0), "Move to start") US_TEST_CONDITION_REQUIRED(rs.good(), "Start re-reading"); std::vector lines; std::string line; while (std::getline(rs, line)) { lines.push_back(line); } US_TEST_CONDITION_REQUIRED(lines.size() > 1, "Number of lines") US_TEST_CONDITION(lines[0] == "foo and", "Check first line") US_TEST_CONDITION(lines[1] == "bar", "Check second line") } void testTextResourceAsBinary(Module* module) { ModuleResource res = module->GetResource("foo.txt"); #ifdef US_PLATFORM_WINDOWS checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 16, false); const std::streampos ssize(16); const std::string fileData = "foo and\r\nbar\r\n\r\n"; #else checkResourceInfo(res, "/", "foo", "foo", "txt", "txt", 13, false); const std::streampos ssize(13); const std::string fileData = "foo and\nbar\n\n"; #endif ModuleResourceStream rs(res, std::ios_base::binary); rs.seekg(0, std::ios::end); US_TEST_CONDITION(rs.tellg() == ssize, "Stream content length"); rs.seekg(0, std::ios::beg); std::string content; content.reserve(res.GetSize()); char buffer[1024]; while (rs.read(buffer, sizeof(buffer))) { content.append(buffer, sizeof(buffer)); } content.append(buffer, static_cast(rs.gcount())); US_TEST_CONDITION(rs.eof(), "EOF check"); US_TEST_CONDITION(content == fileData, "Resource content"); } -#ifdef US_BUILD_SHARED_LIBS void testInvalidResource(Module* module) { ModuleResource res = module->GetResource("invalid"); US_TEST_CONDITION_REQUIRED(res.IsValid() == false, "Check invalid resource") US_TEST_CONDITION(res.GetName().empty(), "Check empty name") US_TEST_CONDITION(res.GetPath().empty(), "Check empty path") US_TEST_CONDITION(res.GetResourcePath().empty(), "Check empty resource path") US_TEST_CONDITION(res.GetBaseName().empty(), "Check empty base name") US_TEST_CONDITION(res.GetCompleteBaseName().empty(), "Check empty complete base name") US_TEST_CONDITION(res.GetSuffix().empty(), "Check empty suffix") US_TEST_CONDITION(res.GetChildren().empty(), "Check empty children") US_TEST_CONDITION(res.GetSize() == 0, "Check zero size") - US_TEST_CONDITION(res.GetData() == NULL, "Check NULL data") ModuleResourceStream rs(res); US_TEST_CONDITION(rs.good() == true, "Check invalid resource stream") rs.ignore(); US_TEST_CONDITION(rs.good() == false, "Check invalid resource stream") US_TEST_CONDITION(rs.eof() == true, "Check invalid resource stream") } -#endif void testSpecialCharacters(Module* module) { ModuleResource res = module->GetResource("special_chars.dummy.txt"); #ifdef US_PLATFORM_WINDOWS checkResourceInfo(res, "/", "special_chars", "special_chars.dummy", "txt", "dummy.txt", 56, false); const std::streampos ssize(54); const std::string fileData = "German Füße (feet)\nFrench garçon de café (waiter)\n"; #else checkResourceInfo(res, "/", "special_chars", "special_chars.dummy", "txt", "dummy.txt", 54, false); const std::streampos ssize(53); const std::string fileData = "German Füße (feet)\nFrench garçon de café (waiter)"; #endif ModuleResourceStream rs(res); rs.seekg(0, std::ios_base::end); US_TEST_CONDITION(rs.tellg() == ssize, "Stream content length"); rs.seekg(0, std::ios_base::beg); std::string content; content.reserve(res.GetSize()); char buffer[1024]; while (rs.read(buffer, sizeof(buffer))) { content.append(buffer, sizeof(buffer)); } content.append(buffer, static_cast(rs.gcount())); US_TEST_CONDITION(rs.eof(), "EOF check"); US_TEST_CONDITION(content == fileData, "Resource content"); } void testBinaryResource(Module* module) { ModuleResource res = module->GetResource("/icons/cppmicroservices.png"); checkResourceInfo(res, "/icons/", "cppmicroservices", "cppmicroservices", "png", "png", 2424, false); ModuleResourceStream rs(res, std::ios_base::binary); rs.seekg(0, std::ios_base::end); std::streampos resLength = rs.tellg(); rs.seekg(0); std::ifstream png(US_CORE_SOURCE_DIR "/test/modules/libRWithResources/resources/icons/cppmicroservices.png", std::ifstream::in | std::ifstream::binary); US_TEST_CONDITION_REQUIRED(png.is_open(), "Open reference file") png.seekg(0, std::ios_base::end); std::streampos pngLength = png.tellg(); png.seekg(0); US_TEST_CONDITION(res.GetSize() == resLength, "Check resource size") US_TEST_CONDITION_REQUIRED(resLength == pngLength, "Compare sizes") char c1 = 0; char c2 = 0; bool isEqual = true; int count = 0; while (png.get(c1) && rs.get(c2)) { ++count; if (c1 != c2) { isEqual = false; break; } } US_TEST_CONDITION_REQUIRED(count == pngLength, "Check if everything was read"); US_TEST_CONDITION_REQUIRED(isEqual, "Equal binary contents"); US_TEST_CONDITION(png.eof(), "EOF check"); } -#ifdef US_ENABLE_RESOURCE_COMPRESSION void testCompressedResource(Module* module) { ModuleResource res = module->GetResource("/icons/compressable.bmp"); - checkResourceInfo(res, "/icons/", "compressable", "compressable", "bmp", "bmp", 411, false, true); + checkResourceInfo(res, "/icons/", "compressable", "compressable", "bmp", "bmp", 300122, false); ModuleResourceStream rs(res, std::ios_base::binary); rs.seekg(0, std::ios_base::end); std::streampos resLength = rs.tellg(); rs.seekg(0); std::ifstream bmp(US_CORE_SOURCE_DIR "/test/modules/libRWithResources/resources/icons/compressable.bmp", std::ifstream::in | std::ifstream::binary); US_TEST_CONDITION_REQUIRED(bmp.is_open(), "Open reference file") bmp.seekg(0, std::ios_base::end); std::streampos bmpLength = bmp.tellg(); bmp.seekg(0); US_TEST_CONDITION(300122 == resLength, "Check resource size") US_TEST_CONDITION_REQUIRED(resLength == bmpLength, "Compare sizes") char c1 = 0; char c2 = 0; bool isEqual = true; int count = 0; while (bmp.get(c1) && rs.get(c2)) { ++count; if (c1 != c2) { isEqual = false; break; } } US_TEST_CONDITION_REQUIRED(count == bmpLength, "Check if everything was read"); US_TEST_CONDITION_REQUIRED(isEqual, "Equal binary contents"); US_TEST_CONDITION(bmp.eof(), "EOF check"); } -#endif struct ResourceComparator { bool operator()(const ModuleResource& mr1, const ModuleResource& mr2) const { return mr1 < mr2; } }; -#ifdef US_BUILD_SHARED_LIBS void testResourceTree(Module* module) { ModuleResource res = module->GetResource(""); US_TEST_CONDITION(res.GetResourcePath() == "/", "Check root file path") US_TEST_CONDITION(res.IsDir() == true, "Check type") std::vector children = res.GetChildren(); std::sort(children.begin(), children.end()); - US_TEST_CONDITION_REQUIRED(children.size() == 4, "Check child count") + US_TEST_CONDITION_REQUIRED(children.size() == 5, "Check child count") US_TEST_CONDITION(children[0] == "foo.txt", "Check child name") - US_TEST_CONDITION(children[1] == "icons", "Check child name") - US_TEST_CONDITION(children[2] == "special_chars.dummy.txt", "Check child name") - US_TEST_CONDITION(children[3] == "test.xml", "Check child name") + US_TEST_CONDITION(children[1] == "foo2.txt", "Check child name") + US_TEST_CONDITION(children[2] == "icons/", "Check child name") + US_TEST_CONDITION(children[3] == "special_chars.dummy.txt", "Check child name") + US_TEST_CONDITION(children[4] == "test.xml", "Check child name") + + US_TEST_CONDITION(module->FindResources("!$noexist=?", std::string(), "true").empty(), "Check not existant path"); ModuleResource readme = module->GetResource("/icons/readme.txt"); US_TEST_CONDITION(readme.IsFile() && readme.GetChildren().empty(), "Check file resource") - ModuleResource icons = module->GetResource("icons"); + ModuleResource icons = module->GetResource("icons/"); US_TEST_CONDITION(icons.IsDir() && !icons.IsFile() && !icons.GetChildren().empty(), "Check directory resource") children = icons.GetChildren(); US_TEST_CONDITION_REQUIRED(children.size() == 3, "Check icons child count") std::sort(children.begin(), children.end()); US_TEST_CONDITION(children[0] == "compressable.bmp", "Check child name") US_TEST_CONDITION(children[1] == "cppmicroservices.png", "Check child name") US_TEST_CONDITION(children[2] == "readme.txt", "Check child name") ResourceComparator resourceComparator; // find all .txt files std::vector nodes = module->FindResources("", "*.txt", false); std::sort(nodes.begin(), nodes.end(), resourceComparator); - US_TEST_CONDITION_REQUIRED(nodes.size() == 2, "Found child count") - US_TEST_CONDITION(nodes[0].GetResourcePath() == "/foo.txt", "Check child name") - US_TEST_CONDITION(nodes[1].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") - - nodes = module->FindResources("", "*.txt", true); - std::sort(nodes.begin(), nodes.end(), resourceComparator); US_TEST_CONDITION_REQUIRED(nodes.size() == 3, "Found child count") US_TEST_CONDITION(nodes[0].GetResourcePath() == "/foo.txt", "Check child name") - US_TEST_CONDITION(nodes[1].GetResourcePath() == "/icons/readme.txt", "Check child name") + US_TEST_CONDITION(nodes[1].GetResourcePath() == "/foo2.txt", "Check child name") US_TEST_CONDITION(nodes[2].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") - // find all resources - nodes = module->FindResources("", "", true); - US_TEST_CONDITION(nodes.size() == 6, "Total resource number") - nodes = module->FindResources("", "**", true); - US_TEST_CONDITION(nodes.size() == 6, "Total resource number") - - - // test pattern matching - nodes.clear(); - nodes = module->FindResources("/icons", "*micro*.png", false); - US_TEST_CONDITION(nodes.size() == 1 && nodes[0].GetResourcePath() == "/icons/cppmicroservices.png", "Check file pattern matches") - - nodes.clear(); - nodes = module->FindResources("", "*.txt", true); - US_TEST_CONDITION(nodes.size() == 3, "Check recursive pattern matches") -} - -#else - -void testResourceTree(Module* module) -{ - ModuleResource res = module->GetResource(""); - US_TEST_CONDITION(res.GetResourcePath() == "/", "Check root file path") - US_TEST_CONDITION(res.IsDir() == true, "Check type") - - std::vector children = res.GetChildren(); - std::sort(children.begin(), children.end()); - US_TEST_CONDITION_REQUIRED(children.size() == 10, "Check child count") - US_TEST_CONDITION(children[0] == "dynamic.txt", "Check dynamic.txt child name") - US_TEST_CONDITION(children[1] == "foo.txt", "Check foo.txt child name") - US_TEST_CONDITION(children[2] == "icons", "Check icons child name") - US_TEST_CONDITION(children[3] == "manifest.json", "Check manifest.json child name") - US_TEST_CONDITION(children[4] == "manifest.json", "Check manifest.json child name") - US_TEST_CONDITION(children[5] == "res.txt", "Check res.txt child name") - US_TEST_CONDITION(children[6] == "res.txt", "Check res.txt child name") - US_TEST_CONDITION(children[7] == "special_chars.dummy.txt", "Check special_chars.dummy.txt child name") - US_TEST_CONDITION(children[8] == "static.txt", "Check static.txt child name") - US_TEST_CONDITION(children[9] == "test.xml", "Check test.xml child name") - - - ModuleResource readme = module->GetResource("/icons/readme.txt"); - US_TEST_CONDITION(readme.IsFile() && readme.GetChildren().empty(), "Check file resource") - - ModuleResource icons = module->GetResource("icons"); - US_TEST_CONDITION(icons.IsDir() && !icons.IsFile() && !icons.GetChildren().empty(), "Check directory resource") - - children = icons.GetChildren(); - US_TEST_CONDITION_REQUIRED(children.size() == 3, "Check icons child count") - std::sort(children.begin(), children.end()); - US_TEST_CONDITION(children[0] == "compressable.bmp", "Check child name") - US_TEST_CONDITION(children[1] == "cppmicroservices.png", "Check child name") - US_TEST_CONDITION(children[2] == "readme.txt", "Check child name") - - ResourceComparator resourceComparator; - - // find all .txt files - std::vector nodes = module->FindResources("", "*.txt", false); - std::sort(nodes.begin(), nodes.end(), resourceComparator); - US_TEST_CONDITION_REQUIRED(nodes.size() == 6, "Found child count") - US_TEST_CONDITION(nodes[0].GetResourcePath() == "/dynamic.txt", "Check dynamic.txt child name") - US_TEST_CONDITION(nodes[1].GetResourcePath() == "/foo.txt", "Check child name") - US_TEST_CONDITION(nodes[2].GetResourcePath() == "/res.txt", "Check res.txt child name") - US_TEST_CONDITION(nodes[3].GetResourcePath() == "/res.txt", "Check res.txt child name") - US_TEST_CONDITION(nodes[4].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") - US_TEST_CONDITION(nodes[5].GetResourcePath() == "/static.txt", "Check static.txt child name") - nodes = module->FindResources("", "*.txt", true); std::sort(nodes.begin(), nodes.end(), resourceComparator); - US_TEST_CONDITION_REQUIRED(nodes.size() == 7, "Found child count") - US_TEST_CONDITION(nodes[0].GetResourcePath() == "/dynamic.txt", "Check dynamic.txt child name") - US_TEST_CONDITION(nodes[1].GetResourcePath() == "/foo.txt", "Check child name") + US_TEST_CONDITION_REQUIRED(nodes.size() == 4, "Found child count") + US_TEST_CONDITION(nodes[0].GetResourcePath() == "/foo.txt", "Check child name") + US_TEST_CONDITION(nodes[1].GetResourcePath() == "/foo2.txt", "Check child name") US_TEST_CONDITION(nodes[2].GetResourcePath() == "/icons/readme.txt", "Check child name") - US_TEST_CONDITION(nodes[3].GetResourcePath() == "/res.txt", "Check res.txt child name") - US_TEST_CONDITION(nodes[4].GetResourcePath() == "/res.txt", "Check res.txt child name") - US_TEST_CONDITION(nodes[5].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") - US_TEST_CONDITION(nodes[6].GetResourcePath() == "/static.txt", "Check static.txt child name") + US_TEST_CONDITION(nodes[3].GetResourcePath() == "/special_chars.dummy.txt", "Check child name") // find all resources nodes = module->FindResources("", "", true); - US_TEST_CONDITION(nodes.size() == 12, "Total resource number") + US_TEST_CONDITION(nodes.size() == 8, "Total resource number") nodes = module->FindResources("", "**", true); - US_TEST_CONDITION(nodes.size() == 12, "Total resource number") + US_TEST_CONDITION(nodes.size() == 8, "Total resource number") // test pattern matching nodes.clear(); nodes = module->FindResources("/icons", "*micro*.png", false); US_TEST_CONDITION(nodes.size() == 1 && nodes[0].GetResourcePath() == "/icons/cppmicroservices.png", "Check file pattern matches") nodes.clear(); nodes = module->FindResources("", "*.txt", true); - US_TEST_CONDITION(nodes.size() == 7, "Check recursive pattern matches") + US_TEST_CONDITION(nodes.size() == 4, "Check recursive pattern matches") } -#endif - void testResourceOperators(Module* module) { ModuleResource invalid = module->GetResource("invalid"); ModuleResource foo = module->GetResource("foo.txt"); US_TEST_CONDITION_REQUIRED(foo.IsValid() && foo, "Check valid resource") ModuleResource foo2(foo); US_TEST_CONDITION(foo == foo, "Check equality operator") US_TEST_CONDITION(foo == foo2, "Check copy constructor and equality operator") US_TEST_CONDITION(foo != invalid, "Check inequality with invalid resource") ModuleResource xml = module->GetResource("/test.xml"); US_TEST_CONDITION_REQUIRED(xml.IsValid() && xml, "Check valid resource") US_TEST_CONDITION(foo != xml, "Check inequality") US_TEST_CONDITION(foo < xml, "Check operator<") // check operator< by using a set std::set resources; resources.insert(foo); resources.insert(foo); resources.insert(xml); US_TEST_CONDITION(resources.size() == 2, "Check operator< with set") // check hash function specialization US_UNORDERED_SET_TYPE resources2; resources2.insert(foo); resources2.insert(foo); resources2.insert(xml); US_TEST_CONDITION(resources2.size() == 2, "Check operator< with unordered set") // check operator<< std::ostringstream oss; oss << foo; US_TEST_CONDITION(oss.str() == foo.GetResourcePath(), "Check operator<<") } -#ifdef US_BUILD_SHARED_LIBS void testResourceFromExecutable(Module* module) { ModuleResource resource = module->GetResource("usTestResource.txt"); US_TEST_CONDITION_REQUIRED(resource.IsValid(), "Check valid executable resource") std::string line; ModuleResourceStream rs(resource); std::getline(rs, line); US_TEST_CONDITION(line == "meant to be compiled into the test driver", "Check executable resource content") } -#endif } // end unnamed namespace int usModuleResourceTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleResourceTest"); ModuleContext* mc = GetModuleContext(); assert(mc); #ifdef US_BUILD_SHARED_LIBS #ifdef US_PLATFORM_WINDOWS const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif SharedLibrary libR(LIB_PATH, "TestModuleR"); try { libR.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } +#endif - Module* moduleR = ModuleRegistry::GetModule("TestModuleR Module"); + Module* moduleR = ModuleRegistry::GetModule("TestModuleR"); US_TEST_CONDITION_REQUIRED(moduleR != NULL, "Test for existing module TestModuleR") - US_TEST_CONDITION(moduleR->GetName() == "TestModuleR Module", "Test module name") + US_TEST_CONDITION(moduleR->GetName() == "TestModuleR", "Test module name") testInvalidResource(moduleR); - testResourceFromExecutable(mc->GetModule()); -#else - Module* moduleR = mc->GetModule(); - US_TEST_CONDITION_REQUIRED(moduleR != NULL, "Test for existing module 0") - US_TEST_CONDITION(moduleR->GetName() == "CppMicroServices", "Test module name") -#endif + testResourceFromExecutable(mc->GetModule()); testResourceTree(moduleR); testResourceOperators(moduleR); testTextResource(moduleR); testTextResourceAsBinary(moduleR); testSpecialCharacters(moduleR); testBinaryResource(moduleR); -#ifdef US_ENABLE_RESOURCE_COMPRESSION testCompressedResource(moduleR); -#endif -#ifdef US_BUILD_SHARED_LIBS ModuleResource foo = moduleR->GetResource("foo.txt"); US_TEST_CONDITION(foo.IsValid() == true, "Valid resource") +#ifdef US_BUILD_SHARED_LIBS libR.Unload(); - US_TEST_CONDITION(foo.IsValid() == false, "Invalidated resource") - US_TEST_CONDITION(foo.GetData() == NULL, "NULL data") + US_TEST_CONDITION(foo.IsValid() == true, "Still valid resource") #endif US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usModuleTest.cpp b/Core/CppMicroServices/core/test/usModuleTest.cpp index 2194399d9b..28f024804b 100644 --- a/Core/CppMicroServices/core/test/usModuleTest.cpp +++ b/Core/CppMicroServices/core/test/usModuleTest.cpp @@ -1,343 +1,350 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include #include "usTestUtilModuleListener.h" +#include "usTestDriverActivator.h" #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; static const char PATH_SEPARATOR = '\\'; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; static const char PATH_SEPARATOR = '/'; #endif +// Check that the executable's activator was loaded and called +void frame01() +{ + US_TEST_CONDITION_REQUIRED(TestDriverActivator::LoadCalled(), "ModuleActivator::Load() called for executable") +} + // Verify that the same member function pointers registered as listeners // with different receivers works. void frame02a() { ModuleContext* mc = GetModuleContext(); TestModuleListener listener1; TestModuleListener listener2; try { mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged); mc->AddModuleListener(&listener1, &TestModuleListener::ModuleChanged); mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged); mc->AddModuleListener(&listener2, &TestModuleListener::ModuleChanged); } catch (const std::logic_error& ise) { US_TEST_FAILED_MSG( << "module listener registration failed " << ise.what() << " : frameSL02a:FAIL" ); } SharedLibrary target(LIB_PATH, "TestModuleA"); #ifdef US_BUILD_SHARED_LIBS // Start the test target try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() << " + in frameSL02a:FAIL" ); } +#endif - Module* moduleA = ModuleRegistry::GetModule("TestModuleA Module"); + Module* moduleA = ModuleRegistry::GetModule("TestModuleA"); US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") -#endif std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA)); #endif std::vector seEvts; US_TEST_CONDITION(listener1.CheckListenerEvents(pEvts, seEvts), "Check first module listener") US_TEST_CONDITION(listener2.CheckListenerEvents(pEvts, seEvts), "Check second module listener") mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged); mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged); target.Unload(); } // Verify information from the ModuleInfo struct void frame005a(ModuleContext* mc) { Module* m = mc->GetModule(); // check expected headers -#ifdef US_BUILD_SHARED_LIBS - US_TEST_CONDITION("usCoreTestDriver" == m->GetName(), "Test module name") + US_TEST_CONDITION("main" == m->GetName(), "Test module name") US_TEST_CONDITION(ModuleVersion(0,1,0) == m->GetVersion(), "Test test driver module version") US_TEST_CONDITION(ModuleVersion(CppMicroServices_MAJOR_VERSION, CppMicroServices_MINOR_VERSION, CppMicroServices_PATCH_VERSION) == ModuleRegistry::GetModule(1)->GetVersion(), "Test CppMicroServices version") -#else - US_TEST_CONDITION("CppMicroServices" == m->GetName(), "Test module name") - US_TEST_CONDITION(ModuleVersion(US_CORE_MAJOR_VERSION, US_CORE_MINOR_VERSION, US_CORE_PATCH_VERSION) == m->GetVersion(), "Test module version") -#endif } // Get context id, location and status of the module void frame010a(ModuleContext* mc) { Module* m = mc->GetModule(); long int contextid = m->GetModuleId(); US_DEBUG << "CONTEXT ID:" << contextid; std::string location = m->GetLocation(); US_DEBUG << "LOCATION:" << location; US_TEST_CONDITION(!location.empty(), "Test for non-empty module location") US_TEST_CONDITION(m->IsLoaded(), "Test for loaded flag") US_TEST_CONDITION(ModuleSettings::GetStoragePath().empty(), "Test for empty base storage path") US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("").empty(), "Test for empty data path") US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("bla").empty(), "Test for empty data file path") } //---------------------------------------------------------------------------- //Test result of GetService(ServiceReference()). Should throw std::invalid_argument void frame018a(ModuleContext* mc) { try { mc->GetService(ServiceReferenceU()); US_DEBUG << "Got service object, expected std::invalid_argument exception"; US_TEST_FAILED_MSG(<< "Got service object, excpected std::invalid_argument exception") } catch (const std::invalid_argument& ) {} catch (...) { US_TEST_FAILED_MSG(<< "Got wrong exception, expected std::invalid_argument") } } // Load libA and check that it exists and that the service it registers exists, // also check that the expected events occur and that the storage paths are correct void frame020a(ModuleContext* mc, TestModuleListener& listener, #ifdef US_BUILD_SHARED_LIBS SharedLibrary& libA) { - ModuleSettings::SetStoragePath(std::string("/tmp") + PATH_SEPARATOR); - US_TEST_CONDITION(ModuleSettings::GetStoragePath() == "/tmp", "Test for valid base storage path") - try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } +#else + SharedLibrary& /*libA*/) +{ +#endif - Module* moduleA = ModuleRegistry::GetModule("TestModuleA Module"); + ModuleSettings::SetStoragePath(std::string("/tmp") + PATH_SEPARATOR); + US_TEST_CONDITION(ModuleSettings::GetStoragePath() == "/tmp", "Test for valid base storage path") + + Module* moduleA = ModuleRegistry::GetModule("TestModuleA"); US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") - US_TEST_CONDITION(moduleA->GetName() == "TestModuleA Module", "Test module name") + US_TEST_CONDITION(moduleA->GetName() == "TestModuleA", "Test module name") - std::cout << moduleA->GetModuleContext()->GetDataFile(""); + std::cout << moduleA->GetModuleContext()->GetDataFile("") << std::endl; std::stringstream ss; ss << moduleA->GetModuleId(); const std::string baseStoragePath = std::string("/tmp") + PATH_SEPARATOR + ss.str() + "_TestModuleA" + PATH_SEPARATOR; US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("") == baseStoragePath, "Test for valid data path") US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("bla") == baseStoragePath + "bla", "Test for valid data file path") -#else - SharedLibrary& /*libA*/) -{ -#endif // Check if libA registered the expected service try { - ServiceReferenceU sr1 = mc->GetServiceReference("org.cppmicroservices.TestModuleAService"); + ServiceReferenceU sr1 = mc->GetServiceReference("us::TestModuleAService"); InterfaceMap o1 = mc->GetService(sr1); US_TEST_CONDITION(!o1.empty(), "Test if service object found"); try { US_TEST_CONDITION(mc->UngetService(sr1), "Test if Service UnGet returns true"); } catch (const std::logic_error le) { US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what()) } // check the listeners for events std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, sr1)); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } catch (const ServiceException& /*se*/) { US_TEST_FAILED_MSG(<< "test module, expected service not found"); } -#ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly"); -#endif } // Unload libA and check for correct events void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libA) { -#ifdef US_BUILD_SHARED_LIBS - Module* moduleA = ModuleRegistry::GetModule("TestModuleA Module"); + Module* moduleA = ModuleRegistry::GetModule("TestModuleA"); US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for non-null module") -#endif ServiceReferenceU sr1 - = mc->GetServiceReference("org.cppmicroservices.TestModuleAService"); + = mc->GetServiceReference("us::TestModuleAService"); US_TEST_CONDITION(sr1, "Test for valid service reference") try { libA.Unload(); #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleA->IsLoaded() == false, "Test for unloaded state") #endif } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what()) } std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleA)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, sr1)); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } struct LocalListener { void ServiceChanged(const ServiceEvent) {} }; // Add a service listener with a broken LDAP filter to Get an exception void frame045a(ModuleContext* mc) { LocalListener sListen1; std::string brokenFilter = "A broken LDAP filter"; try { mc->AddServiceListener(&sListen1, &LocalListener::ServiceChanged, brokenFilter); } catch (const std::invalid_argument& /*ia*/) { //assertEquals("InvalidSyntaxException.GetFilter should be same as input string", brokenFilter, ise.GetFilter()); } catch (...) { US_TEST_FAILED_MSG(<< "test module, wrong exception on broken LDAP filter:"); } } } // end unnamed namespace int usModuleTest(int /*argc*/, char* /*argv*/[]) { - US_TEST_BEGIN("ModuleTest"); + //US_TEST_BEGIN("ModuleTest"); + + std::vector modules = ModuleRegistry::GetModules(); + for (std::vector::iterator iter = modules.begin(), iterEnd = modules.end(); + iter != iterEnd; ++iter) + { + std::cout << "----- " << (*iter)->GetName() << std::endl; + } + frame01(); frame02a(); ModuleContext* mc = GetModuleContext(); TestModuleListener listener; try { mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); throw; } try { mc->AddServiceListener(&listener, &TestModuleListener::ServiceChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "service listener registration failed " << ise.what() ); throw; } frame005a(mc); frame010a(mc); frame018a(mc); SharedLibrary libA(LIB_PATH, "TestModuleA"); frame020a(mc, listener, libA); frame030b(mc, listener, libA); frame045a(mc); mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged); mc->RemoveServiceListener(&listener, &TestModuleListener::ServiceChanged); - US_TEST_END() + //US_TEST_END() + return 0; } diff --git a/Core/CppMicroServices/core/test/usServiceControlInterface.h b/Core/CppMicroServices/core/test/usServiceControlInterface.h index c6172a4778..5ed36ca6bd 100644 --- a/Core/CppMicroServices/core/test/usServiceControlInterface.h +++ b/Core/CppMicroServices/core/test/usServiceControlInterface.h @@ -1,45 +1,43 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICECONTROLINTERFACE_H #define USSERVICECONTROLINTERFACE_H #include #include #include US_BEGIN_NAMESPACE struct ServiceControlInterface { virtual ~ServiceControlInterface() {} virtual void ServiceControl(int service, const std::string& operation, int ranking) = 0; }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(ServiceControlInterface), "org.us.testing.ServiceControlInterface") - #endif // USSERVICECONTROLINTERFACE_H diff --git a/Core/CppMicroServices/core/test/usServiceFactoryTest.cpp b/Core/CppMicroServices/core/test/usServiceFactoryTest.cpp index fa75a65922..1a7d0bde39 100644 --- a/Core/CppMicroServices/core/test/usServiceFactoryTest.cpp +++ b/Core/CppMicroServices/core/test/usServiceFactoryTest.cpp @@ -1,236 +1,231 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif } // end unnamed namespace US_BEGIN_NAMESPACE struct TestModuleH { virtual ~TestModuleH() {} }; struct TestModuleH2 { virtual ~TestModuleH2() {} }; US_END_NAMESPACE -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleH), "org.cppmicroservices.TestModuleH") -US_DECLARE_SERVICE_INTERFACE(US_PREPEND_NAMESPACE(TestModuleH2), "org.cppmicroservices.TestModuleH2") - void TestServiceFactoryModuleScope() { // Install and start test module H, a service factory and test that the methods // in that interface works. SharedLibrary target(LIB_PATH, "TestModuleH"); #ifdef US_BUILD_SHARED_LIBS try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what()) } +#endif - Module* moduleH = ModuleRegistry::GetModule("TestModuleH Module"); + Module* moduleH = ModuleRegistry::GetModule("TestModuleH"); US_TEST_CONDITION_REQUIRED(moduleH != 0, "Test for existing module TestModuleH") std::vector registeredRefs = moduleH->GetRegisteredServices(); US_TEST_CONDITION_REQUIRED(registeredRefs.size() == 2, "# of registered services") US_TEST_CONDITION(registeredRefs[0].GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_MODULE(), "service scope") US_TEST_CONDITION(registeredRefs[1].GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_PROTOTYPE(), "service scope") -#endif ModuleContext* mc = GetModuleContext(); // Check that a service reference exist - const ServiceReferenceU sr1 = mc->GetServiceReference("org.cppmicroservices.TestModuleH"); + const ServiceReferenceU sr1 = mc->GetServiceReference("us::TestModuleH"); US_TEST_CONDITION_REQUIRED(sr1, "Service shall be present.") US_TEST_CONDITION(sr1.GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_MODULE(), "service scope") InterfaceMap service = mc->GetService(sr1); US_TEST_CONDITION_REQUIRED(service.size() >= 1, "GetService()") - InterfaceMap::const_iterator serviceIter = service.find("org.cppmicroservices.TestModuleH"); + InterfaceMap::const_iterator serviceIter = service.find("us::TestModuleH"); US_TEST_CONDITION_REQUIRED(serviceIter != service.end(), "GetService()") US_TEST_CONDITION_REQUIRED(serviceIter->second != NULL, "GetService()") InterfaceMap service2 = mc->GetService(sr1); US_TEST_CONDITION(service == service2, "Same service pointer") -#ifdef US_BUILD_SHARED_LIBS std::vector usedRefs = mc->GetModule()->GetServicesInUse(); US_TEST_CONDITION_REQUIRED(usedRefs.size() == 1, "services in use") US_TEST_CONDITION(usedRefs[0] == sr1, "service ref in use") InterfaceMap service3 = moduleH->GetModuleContext()->GetService(sr1); US_TEST_CONDITION(service != service3, "Different service pointer") US_TEST_CONDITION(moduleH->GetModuleContext()->UngetService(sr1), "UngetService()") -#endif US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == false, "ungetService()") US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == true, "ungetService()") target.Unload(); } void TestServiceFactoryPrototypeScope() { // Install and start test module H, a service factory and test that the methods // in that interface works. SharedLibrary target(LIB_PATH, "TestModuleH"); #ifdef US_BUILD_SHARED_LIBS try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what()) } - Module* moduleH = ModuleRegistry::GetModule("TestModuleH Module"); + Module* moduleH = ModuleRegistry::GetModule("TestModuleH"); US_TEST_CONDITION_REQUIRED(moduleH != 0, "Test for existing module TestModuleH") #endif ModuleContext* mc = GetModuleContext(); // Check that a service reference exist const ServiceReference sr1 = mc->GetServiceReference(); US_TEST_CONDITION_REQUIRED(sr1, "Service shall be present.") US_TEST_CONDITION(sr1.GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_PROTOTYPE(), "service scope") ServiceObjects svcObjects = mc->GetServiceObjects(sr1); TestModuleH2* prototypeServiceH2 = svcObjects.GetService(); const ServiceReferenceU sr1void = mc->GetServiceReference(us_service_interface_iid()); ServiceObjects svcObjectsVoid = mc->GetServiceObjects(sr1void); InterfaceMap prototypeServiceH2Void = svcObjectsVoid.GetService(); US_TEST_CONDITION_REQUIRED(prototypeServiceH2Void.find(us_service_interface_iid()) != prototypeServiceH2Void.end(), "ServiceObjects::GetService()") #ifdef US_BUILD_SHARED_LIBS // There should be only one service in use US_TEST_CONDITION_REQUIRED(mc->GetModule()->GetServicesInUse().size() == 1, "services in use") #endif TestModuleH2* moduleScopeService = mc->GetService(sr1); US_TEST_CONDITION_REQUIRED(moduleScopeService && moduleScopeService != prototypeServiceH2, "GetService()") US_TEST_CONDITION_REQUIRED(prototypeServiceH2 != prototypeServiceH2Void.find(us_service_interface_iid())->second, "GetService()") svcObjectsVoid.UngetService(prototypeServiceH2Void); TestModuleH2* moduleScopeService2 = mc->GetService(sr1); US_TEST_CONDITION(moduleScopeService == moduleScopeService2, "Same service pointer") #ifdef US_BUILD_SHARED_LIBS std::vector usedRefs = mc->GetModule()->GetServicesInUse(); US_TEST_CONDITION_REQUIRED(usedRefs.size() == 1, "services in use") US_TEST_CONDITION(usedRefs[0] == sr1, "service ref in use") #endif std::string filter = "(" + ServiceConstants::SERVICE_ID() + "=" + sr1.GetProperty(ServiceConstants::SERVICE_ID()).ToString() + ")"; const ServiceReference sr2 = mc->GetServiceReferences(filter).front(); US_TEST_CONDITION_REQUIRED(sr2, "Service shall be present.") US_TEST_CONDITION(sr2.GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_PROTOTYPE(), "service scope") US_TEST_CONDITION(any_cast(sr2.GetProperty(ServiceConstants::SERVICE_ID())) == any_cast(sr1.GetProperty(ServiceConstants::SERVICE_ID())), "same service id") try { svcObjects.UngetService(moduleScopeService2); US_TEST_FAILED_MSG(<< "std::invalid_argument exception expected") } catch (const std::invalid_argument&) { // this is expected } #ifdef US_BUILD_SHARED_LIBS // There should still be only one service in use usedRefs = mc->GetModule()->GetServicesInUse(); US_TEST_CONDITION_REQUIRED(usedRefs.size() == 1, "services in use") #endif ServiceObjects svcObjects2 = svcObjects; ServiceObjects svcObjects3 = mc->GetServiceObjects(sr1); try { svcObjects3.UngetService(prototypeServiceH2); US_TEST_FAILED_MSG(<< "std::invalid_argument exception expected") } catch (const std::invalid_argument&) { // this is expected } svcObjects2.UngetService(prototypeServiceH2); prototypeServiceH2 = svcObjects2.GetService(); TestModuleH2* prototypeServiceH2_2 = svcObjects3.GetService(); US_TEST_CONDITION_REQUIRED(prototypeServiceH2_2 && prototypeServiceH2_2 != prototypeServiceH2, "prototype service") svcObjects2.UngetService(prototypeServiceH2); svcObjects3.UngetService(prototypeServiceH2_2); US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == false, "ungetService()") US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == true, "ungetService()") target.Unload(); } int usServiceFactoryTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceFactoryTest"); TestServiceFactoryModuleScope(); TestServiceFactoryPrototypeScope(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usServiceHooksTest.cpp b/Core/CppMicroServices/core/test/usServiceHooksTest.cpp index a9821498bc..be44b68a4c 100644 --- a/Core/CppMicroServices/core/test/usServiceHooksTest.cpp +++ b/Core/CppMicroServices/core/test/usServiceHooksTest.cpp @@ -1,414 +1,414 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif class TestServiceListener { public: void ServiceChanged(const ServiceEvent serviceEvent) { this->events.push_back(serviceEvent); } std::vector events; }; class TestServiceEventListenerHook : public ServiceEventListenerHook { private: int id; public: TestServiceEventListenerHook(int id) : id(id) { } typedef ShrinkableMap > MapType; void Event(const ServiceEvent& /*event*/, MapType& listeners) { US_TEST_CONDITION_REQUIRED(listeners.size() > 0 && listeners.find(GetModuleContext()) != listeners.end(), "Check listener content"); ShrinkableVector& listenerInfos = listeners[GetModuleContext()]; // listener count should be 2 because the event listener hooks are called with // the list of listeners before filtering them according to ther LDAP filter if (id == 1) { #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(listenerInfos.size() == 2, "2 service listeners expected"); #else US_TEST_CONDITION(listenerInfos.size() >= 2, "2 service listeners expected"); #endif US_TEST_CONDITION(listenerInfos[0].IsRemoved() == false, "Listener is not removed"); US_TEST_CONDITION(listenerInfos[1].IsRemoved() == false, "Listener is not removed"); US_TEST_CONDITION(!(listenerInfos[0] == listenerInfos[1]), "listener info inequality"); } else { // there is already one listener filtered out #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(listenerInfos.size() == 1, "1 service listener expected"); #else US_TEST_CONDITION(listenerInfos.size() >= 1, "1 service listener expected"); #endif US_TEST_CONDITION(listenerInfos[0].IsRemoved() == false, "Listener is not removed"); } if (listenerInfo.IsNull()) { listenerInfo = listenerInfos[0]; } else { US_TEST_CONDITION(listenerInfo == listenerInfos[0], "Equal listener info objects"); } // Remove the listener without a filter from the list for(ShrinkableVector::iterator infoIter = listenerInfos.begin(); infoIter != listenerInfos.end();) { if (infoIter->GetFilter().empty()) { infoIter = listenerInfos.erase(infoIter); } else { ++infoIter; } } #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(listenerInfos.size() == 1, "One listener with LDAP filter should remain"); #else US_TEST_CONDITION(listenerInfos.size() >= 1, "One listener with LDAP filter should remain"); #endif ordering.push_back(id); } ServiceListenerHook::ListenerInfo listenerInfo; static std::vector ordering; }; std::vector TestServiceEventListenerHook::ordering; class TestServiceFindHook : public ServiceFindHook { private: int id; public: TestServiceFindHook(int id) : id(id) { } void Find(const ModuleContext* context, const std::string& /*name*/, const std::string& /*filter*/, ShrinkableVector& references) { US_TEST_CONDITION(context == GetModuleContext(), "Module context"); references.clear(); ordering.push_back(id); } static std::vector ordering; }; std::vector TestServiceFindHook::ordering; class TestServiceListenerHook : public ServiceListenerHook { private: int id; public: TestServiceListenerHook(int id) : id(id) { } void Added(const std::vector& listeners) { for (std::vector::const_iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { if (iter->IsRemoved() || iter->GetModuleContext() != GetModuleContext()) continue; listenerInfos.insert(*iter); lastAdded = listeners.back(); ordering.push_back(id); } } void Removed(const std::vector& listeners) { for (std::vector::const_iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { listenerInfos.erase(*iter); ordering.push_back(id*10); } lastRemoved = listeners.back(); } static std::vector ordering; US_UNORDERED_SET_TYPE listenerInfos; ListenerInfo lastAdded; ListenerInfo lastRemoved; }; std::vector TestServiceListenerHook::ordering; void TestEventListenerHook() { ModuleContext* context = GetModuleContext(); TestServiceListener serviceListener1; TestServiceListener serviceListener2; context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); context->AddServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "bla"); TestServiceEventListenerHook serviceEventListenerHook1(1); ServiceProperties hookProps1; hookProps1[ServiceConstants::SERVICE_RANKING()] = 10; ServiceRegistration eventListenerHookReg1 = context->RegisterService(&serviceEventListenerHook1, hookProps1); TestServiceEventListenerHook serviceEventListenerHook2(2); ServiceProperties hookProps2; hookProps2[ServiceConstants::SERVICE_RANKING()] = 0; ServiceRegistration eventListenerHookReg2 = context->RegisterService(&serviceEventListenerHook2, hookProps2); std::vector expectedOrdering; expectedOrdering.push_back(1); expectedOrdering.push_back(1); expectedOrdering.push_back(2); US_TEST_CONDITION(serviceEventListenerHook1.ordering == expectedOrdering, "Event listener hook call order"); US_TEST_CONDITION(serviceListener1.events.empty(), "service event of service event listener hook"); US_TEST_CONDITION(serviceListener2.events.empty(), "no service event for filtered listener"); #ifdef US_BUILD_SHARED_LIBS SharedLibrary libA(LIB_PATH, "TestModuleA"); try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } expectedOrdering.push_back(1); expectedOrdering.push_back(2); US_TEST_CONDITION(serviceEventListenerHook1.ordering == expectedOrdering, "Event listener hook call order"); libA.Unload(); #endif US_TEST_CONDITION(serviceListener1.events.empty(), "no service event due to service event listener hook"); US_TEST_CONDITION(serviceListener2.events.empty(), "no service event for filtered listener due to service event listener hook"); eventListenerHookReg2.Unregister(); eventListenerHookReg1.Unregister(); context->RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); context->RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged); } void TestListenerHook() { ModuleContext* context = GetModuleContext(); TestServiceListener serviceListener1; TestServiceListener serviceListener2; context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); context->AddServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "bla"); TestServiceListenerHook serviceListenerHook1(1); ServiceProperties hookProps1; hookProps1[ServiceConstants::SERVICE_RANKING()] = 0; ServiceRegistration listenerHookReg1 = context->RegisterService(&serviceListenerHook1, hookProps1); TestServiceListenerHook serviceListenerHook2(2); ServiceProperties hookProps2; hookProps2[ServiceConstants::SERVICE_RANKING()] = 10; ServiceRegistration listenerHookReg2 = context->RegisterService(&serviceListenerHook2, hookProps2); #ifdef US_BUILD_SHARED_LIBS // check if hooks got notified about the existing listeners US_TEST_CONDITION_REQUIRED(serviceListenerHook1.listenerInfos.size() == 2, "Notification about existing listeners") #endif const std::size_t listenerInfoSizeOld = serviceListenerHook1.listenerInfos.size() - 2; context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); ServiceListenerHook::ListenerInfo lastAdded = serviceListenerHook1.lastAdded; #ifdef US_BUILD_SHARED_LIBS std::vector expectedOrdering; expectedOrdering.push_back(1); expectedOrdering.push_back(1); expectedOrdering.push_back(2); expectedOrdering.push_back(2); expectedOrdering.push_back(20); expectedOrdering.push_back(10); expectedOrdering.push_back(2); expectedOrdering.push_back(1); US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order"); #endif context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "blub"); US_TEST_CONDITION(lastAdded == serviceListenerHook1.lastRemoved, "Same ListenerInfo object)"); US_TEST_CONDITION(!(lastAdded == serviceListenerHook1.lastAdded), "New ListenerInfo object)"); #ifdef US_BUILD_SHARED_LIBS expectedOrdering.push_back(20); expectedOrdering.push_back(10); expectedOrdering.push_back(2); expectedOrdering.push_back(1); US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order"); #endif context->RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); context->RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged); #ifdef US_BUILD_SHARED_LIBS expectedOrdering.push_back(20); expectedOrdering.push_back(10); expectedOrdering.push_back(20); expectedOrdering.push_back(10); US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order"); #endif US_TEST_CONDITION_REQUIRED(serviceListenerHook1.listenerInfos.size() == listenerInfoSizeOld, "Removed listener infos") listenerHookReg2.Unregister(); listenerHookReg1.Unregister(); } void TestFindHook() { ModuleContext* context = GetModuleContext(); TestServiceFindHook serviceFindHook1(1); ServiceProperties hookProps1; hookProps1[ServiceConstants::SERVICE_RANKING()] = 0; ServiceRegistration findHookReg1 = context->RegisterService(&serviceFindHook1, hookProps1); TestServiceFindHook serviceFindHook2(2); ServiceProperties hookProps2; hookProps2[ServiceConstants::SERVICE_RANKING()] = 10; ServiceRegistration findHookReg2 = context->RegisterService(&serviceFindHook2, hookProps2); std::vector expectedOrdering; US_TEST_CONDITION(serviceFindHook1.ordering == expectedOrdering, "Find hook call order"); TestServiceListener serviceListener; context->AddServiceListener(&serviceListener, &TestServiceListener::ServiceChanged); #ifdef US_BUILD_SHARED_LIBS SharedLibrary libA(LIB_PATH, "TestModuleA"); try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } US_TEST_CONDITION(serviceListener.events.size() == 1, "Service registered"); #endif - std::vector refs = context->GetServiceReferences("org.cppmicroservices.TestModuleAService"); + std::vector refs = context->GetServiceReferences("us::TestModuleAService"); US_TEST_CONDITION(refs.empty(), "Empty references"); - ServiceReferenceU ref = context->GetServiceReference("org.cppmicroservices.TestModuleAService"); + ServiceReferenceU ref = context->GetServiceReference("us::TestModuleAService"); US_TEST_CONDITION(!ref, "Invalid reference (filtered out)"); expectedOrdering.push_back(2); expectedOrdering.push_back(1); expectedOrdering.push_back(2); expectedOrdering.push_back(1); US_TEST_CONDITION(serviceFindHook1.ordering == expectedOrdering, "Find hook call order"); findHookReg2.Unregister(); findHookReg1.Unregister(); - refs = context->GetServiceReferences("org.cppmicroservices.TestModuleAService"); + refs = context->GetServiceReferences("us::TestModuleAService"); US_TEST_CONDITION(!refs.empty(), "Non-empty references"); - ref = context->GetServiceReference("org.cppmicroservices.TestModuleAService"); + ref = context->GetServiceReference("us::TestModuleAService"); US_TEST_CONDITION(ref, "Valid reference"); #ifdef US_BUILD_SHARED_LIBS libA.Unload(); #endif context->RemoveServiceListener(&serviceListener, &TestServiceListener::ServiceChanged); } } // end unnamed namespace int usServiceHooksTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceHooksTest"); TestListenerHook(); TestFindHook(); TestEventListenerHook(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usServiceRegistryPerformanceTest.cpp b/Core/CppMicroServices/core/test/usServiceRegistryPerformanceTest.cpp index 8de3197608..d1a8f16000 100644 --- a/Core/CppMicroServices/core/test/usServiceRegistryPerformanceTest.cpp +++ b/Core/CppMicroServices/core/test/usServiceRegistryPerformanceTest.cpp @@ -1,424 +1,428 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestingMacros.h" #include #include US_USE_NAMESPACE #ifdef US_PLATFORM_APPLE #include #elif defined(US_PLATFORM_POSIX) #include #include #ifndef _POSIX_MONOTONIC_CLOCK #error Monotonic clock support missing on this POSIX platform #endif #elif defined(US_PLATFORM_WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef VC_EXTRA_LEAN +#define VC_EXTRA_LEAN +#endif #include #else #error High precision timer support nod available on this platform #endif #include class HighPrecisionTimer { public: inline HighPrecisionTimer(); inline void Start(); inline long long ElapsedMilli(); inline long long ElapsedMicro(); private: #ifdef US_PLATFORM_APPLE static double timeConvert; uint64_t startTime; #elif defined(US_PLATFORM_POSIX) timespec startTime; #elif defined(US_PLATFORM_WINDOWS) LARGE_INTEGER timerFrequency; LARGE_INTEGER startTime; #endif }; #ifdef US_PLATFORM_APPLE double HighPrecisionTimer::timeConvert = 0.0; inline HighPrecisionTimer::HighPrecisionTimer() : startTime(0) { if (timeConvert == 0) { mach_timebase_info_data_t timeBase; mach_timebase_info(&timeBase); timeConvert = static_cast(timeBase.numer) / static_cast(timeBase.denom) / 1000.0; } } inline void HighPrecisionTimer::Start() { startTime = mach_absolute_time(); } inline long long HighPrecisionTimer::ElapsedMilli() { uint64_t current = mach_absolute_time(); return static_cast(current - startTime) * timeConvert / 1000.0; } inline long long HighPrecisionTimer::ElapsedMicro() { uint64_t current = mach_absolute_time(); return static_cast(current - startTime) * timeConvert; } #elif defined(US_PLATFORM_POSIX) inline HighPrecisionTimer::HighPrecisionTimer() { startTime.tv_nsec = 0; startTime.tv_sec = 0; } inline void HighPrecisionTimer::Start() { clock_gettime(CLOCK_MONOTONIC, &startTime); } inline long long HighPrecisionTimer::ElapsedMilli() { timespec current; clock_gettime(CLOCK_MONOTONIC, ¤t); return (static_cast(current.tv_sec)*1000 + current.tv_nsec/1000/1000) - (static_cast(startTime.tv_sec)*1000 + startTime.tv_nsec/1000/1000); } inline long long HighPrecisionTimer::ElapsedMicro() { timespec current; clock_gettime(CLOCK_MONOTONIC, ¤t); return (static_cast(current.tv_sec)*1000*1000 + current.tv_nsec/1000) - (static_cast(startTime.tv_sec)*1000*1000 + startTime.tv_nsec/1000); } #elif defined(US_PLATFORM_WINDOWS) inline HighPrecisionTimer::HighPrecisionTimer() { if (!QueryPerformanceFrequency(&timerFrequency)) throw std::runtime_error("QueryPerformanceFrequency() failed"); } inline void HighPrecisionTimer::Start() { //DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0); QueryPerformanceCounter(&startTime); //SetThreadAffinityMask(GetCurrentThread(), oldmask); } inline long long HighPrecisionTimer::ElapsedMilli() { LARGE_INTEGER current; QueryPerformanceCounter(¤t); return (current.QuadPart - startTime.QuadPart) / (timerFrequency.QuadPart / 1000); } inline long long HighPrecisionTimer::ElapsedMicro() { LARGE_INTEGER current; QueryPerformanceCounter(¤t); return (current.QuadPart - startTime.QuadPart) / (timerFrequency.QuadPart / (1000*1000)); } #endif class MyServiceListener; struct IPerfTestService { virtual ~IPerfTestService() {} }; -US_DECLARE_SERVICE_INTERFACE(IPerfTestService, "org.cppmicroservices.test.IPerfTestService") - class ServiceRegistryPerformanceTest { private: friend class MyServiceListener; ModuleContext* mc; int nListeners; int nServices; std::size_t nRegistered; std::size_t nUnregistering; std::size_t nModified; std::vector > regs; std::vector listeners; std::vector services; public: ServiceRegistryPerformanceTest(ModuleContext* context); void InitTestCase(); void CleanupTestCase(); void TestAddListeners(); void TestRegisterServices(); void TestModifyServices(); void TestUnregisterServices(); private: std::ostream& Log() const { return std::cout; } void AddListeners(int n); void RegisterServices(int n); void ModifyServices(); void UnregisterServices(); }; class MyServiceListener { private: ServiceRegistryPerformanceTest* ts; public: MyServiceListener(ServiceRegistryPerformanceTest* ts) : ts(ts) { } void ServiceChanged(const ServiceEvent ev) { switch(ev.GetType()) { case ServiceEvent::REGISTERED: ts->nRegistered++; break; case ServiceEvent::UNREGISTERING: ts->nUnregistering++; break; case ServiceEvent::MODIFIED: ts->nModified++; break; default: break; } } }; ServiceRegistryPerformanceTest::ServiceRegistryPerformanceTest(ModuleContext* context) : mc(context) , nListeners(100) , nServices(1000) , nRegistered(0) , nUnregistering(0) , nModified(0) { } void ServiceRegistryPerformanceTest::InitTestCase() { Log() << "Initialize event counters\n"; nRegistered = 0; nUnregistering = 0; nModified = 0; } void ServiceRegistryPerformanceTest::CleanupTestCase() { Log() << "Remove all service listeners\n"; for(std::size_t i = 0; i < listeners.size(); i++) { try { MyServiceListener* l = listeners[i]; mc->RemoveServiceListener(l, &MyServiceListener::ServiceChanged); delete l; } catch (const std::exception& e) { Log() << e.what(); } } listeners.clear(); } void ServiceRegistryPerformanceTest::TestAddListeners() { AddListeners(nListeners); } void ServiceRegistryPerformanceTest::AddListeners(int n) { Log() << "adding " << n << " service listeners\n"; for(int i = 0; i < n; i++) { MyServiceListener* l = new MyServiceListener(this); try { listeners.push_back(l); mc->AddServiceListener(l, &MyServiceListener::ServiceChanged, "(perf.service.value>=0)"); } catch (const std::exception& e) { Log() << e.what(); } } Log() << "listener count=" << listeners.size() << "\n"; } void ServiceRegistryPerformanceTest::TestRegisterServices() { Log() << "Register services, and check that we get #of services (" << nServices << ") * #of listeners (" << nListeners << ") REGISTERED events\n"; Log() << "registering " << nServices << " services, listener count=" << listeners.size() << "\n"; HighPrecisionTimer t; t.Start(); RegisterServices(nServices); long long ms = t.ElapsedMilli(); Log() << "register took " << ms << "ms\n"; US_TEST_CONDITION_REQUIRED(nServices * listeners.size() == nRegistered, "# REGISTERED events must be same as # of registered services * # of listeners"); } void ServiceRegistryPerformanceTest::RegisterServices(int n) { class PerfTestService : public IPerfTestService { }; std::string pid("my.service."); for(int i = 0; i < n; i++) { ServiceProperties props; std::stringstream ss; ss << pid << i; props["service.pid"] = ss.str(); props["perf.service.value"] = i+1; PerfTestService* service = new PerfTestService(); services.push_back(service); ServiceRegistration reg = mc->RegisterService(service, props); regs.push_back(reg); } } void ServiceRegistryPerformanceTest::TestModifyServices() { Log() << "Modify all services, and check that we get #of services (" << nServices << ") * #of listeners (" << nListeners << ") MODIFIED events\n"; HighPrecisionTimer t; t.Start(); ModifyServices(); long long ms = t.ElapsedMilli(); Log() << "modify took " << ms << "ms\n"; US_TEST_CONDITION_REQUIRED(nServices * listeners.size() == nModified, "# MODIFIED events must be same as # of modified services * # of listeners"); } void ServiceRegistryPerformanceTest::ModifyServices() { Log() << "modifying " << regs.size() << " services, listener count=" << listeners.size() << "\n"; for(std::size_t i = 0; i < regs.size(); i++) { ServiceRegistration reg = regs[i]; ServiceProperties props; props["perf.service.value"] = i * 2; reg.SetProperties(props); } } void ServiceRegistryPerformanceTest::TestUnregisterServices() { Log() << "Unregister all services, and check that we get #of services (" << nServices << ") * #of listeners (" << nListeners << ") UNREGISTERING events\n"; HighPrecisionTimer t; t.Start(); UnregisterServices(); long long ms = t.ElapsedMilli(); Log() << "unregister took " << ms << "ms\n"; US_TEST_CONDITION_REQUIRED(nServices * listeners.size() == nUnregistering, "# UNREGISTERING events must be same as # of (un)registered services * # of listeners"); } void ServiceRegistryPerformanceTest::UnregisterServices() { Log() << "unregistering " << regs.size() << " services, listener count=" << listeners.size() << "\n"; for(std::size_t i = 0; i < regs.size(); i++) { ServiceRegistration reg = regs[i]; reg.Unregister(); } regs.clear(); } int usServiceRegistryPerformanceTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceRegistryPerformanceTest") ServiceRegistryPerformanceTest perfTest(GetModuleContext()); perfTest.InitTestCase(); perfTest.TestAddListeners(); perfTest.TestRegisterServices(); perfTest.TestModifyServices(); perfTest.TestUnregisterServices(); perfTest.CleanupTestCase(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usServiceRegistryTest.cpp b/Core/CppMicroServices/core/test/usServiceRegistryTest.cpp index 8943242082..a755db97bf 100644 --- a/Core/CppMicroServices/core/test/usServiceRegistryTest.cpp +++ b/Core/CppMicroServices/core/test/usServiceRegistryTest.cpp @@ -1,138 +1,140 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include "usTestingMacros.h" #include #include #include #include US_USE_NAMESPACE struct ITestServiceA { virtual ~ITestServiceA() {} }; -US_DECLARE_SERVICE_INTERFACE(ITestServiceA, "org.cppmicroservices.testing.ITestServiceA") -int TestMultipleServiceRegistrations() +void TestServiceInterfaceId() +{ + US_TEST_CONDITION(us_service_interface_iid() == "int", "Service interface id int") + US_TEST_CONDITION(us_service_interface_iid() == "ITestServiceA", "Service interface id ITestServiceA") +} + +void TestMultipleServiceRegistrations() { struct TestServiceA : public ITestServiceA { }; ModuleContext* context = GetModuleContext(); TestServiceA s1; TestServiceA s2; ServiceRegistration reg1 = context->RegisterService(&s1); ServiceRegistration reg2 = context->RegisterService(&s2); std::vector > refs = context->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Testing for two registered ITestServiceA services") reg2.Unregister(); refs = context->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(refs.size() == 1, "Testing for one registered ITestServiceA services") reg1.Unregister(); refs = context->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(refs.empty(), "Testing for no ITestServiceA services") ServiceReference ref = context->GetServiceReference(); US_TEST_CONDITION_REQUIRED(!ref, "Testing for invalid service reference") - - return EXIT_SUCCESS; } -int TestServicePropertiesUpdate() +void TestServicePropertiesUpdate() { struct TestServiceA : public ITestServiceA { }; ModuleContext* context = GetModuleContext(); TestServiceA s1; ServiceProperties props; props["string"] = std::string("A std::string"); props["bool"] = false; const char* str = "A const char*"; props["const char*"] = str; ServiceRegistration reg1 = context->RegisterService(&s1, props); ServiceReference ref1 = context->GetServiceReference(); US_TEST_CONDITION_REQUIRED(context->GetServiceReferences().size() == 1, "Testing service count") US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty("bool")) == false, "Testing bool property") // register second service with higher rank TestServiceA s2; ServiceProperties props2; props2[ServiceConstants::SERVICE_RANKING()] = 50; ServiceRegistration reg2 = context->RegisterService(&s2, props2); // Get the service with the highest rank, this should be s2. ServiceReference ref2 = context->GetServiceReference(); TestServiceA* service = dynamic_cast(context->GetService(ref2)); US_TEST_CONDITION_REQUIRED(service == &s2, "Testing highest service rank") props["bool"] = true; // change the service ranking props[ServiceConstants::SERVICE_RANKING()] = 100; reg1.SetProperties(props); US_TEST_CONDITION_REQUIRED(context->GetServiceReferences().size() == 2, "Testing service count") US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty("bool")) == true, "Testing bool property") US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty(ServiceConstants::SERVICE_RANKING())) == 100, "Testing updated ranking") // Service with the highest ranking should now be s1 service = dynamic_cast(context->GetService(ref1)); US_TEST_CONDITION_REQUIRED(service == &s1, "Testing highest service rank") reg1.Unregister(); US_TEST_CONDITION_REQUIRED(context->GetServiceReferences("").size() == 1, "Testing service count") service = dynamic_cast(context->GetService(ref2)); US_TEST_CONDITION_REQUIRED(service == &s2, "Testing highest service rank") reg2.Unregister(); US_TEST_CONDITION_REQUIRED(context->GetServiceReferences().empty(), "Testing service count") - - return EXIT_SUCCESS; } int usServiceRegistryTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceRegistryTest"); - US_TEST_CONDITION(TestMultipleServiceRegistrations() == EXIT_SUCCESS, "Testing service registrations: ") - US_TEST_CONDITION(TestServicePropertiesUpdate() == EXIT_SUCCESS, "Testing service property update: ") + TestServiceInterfaceId(); + TestMultipleServiceRegistrations(); + TestServicePropertiesUpdate(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usServiceTemplateTest.cpp b/Core/CppMicroServices/core/test/usServiceTemplateTest.cpp index ef7ab792bb..b91dae1bd5 100644 --- a/Core/CppMicroServices/core/test/usServiceTemplateTest.cpp +++ b/Core/CppMicroServices/core/test/usServiceTemplateTest.cpp @@ -1,211 +1,208 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include "usTestingMacros.h" struct Interface1 {}; -US_DECLARE_SERVICE_INTERFACE(Interface1, "org.cppmicroservices.test.Interface1") struct Interface2 {}; -US_DECLARE_SERVICE_INTERFACE(Interface2, "org.cppmicroservices.test.Interface2") struct Interface3 {}; -US_DECLARE_SERVICE_INTERFACE(Interface3, "org.cppmicroservices.test.Interface3") struct MyService1 : public Interface1 {}; struct MyService2 : public Interface1, public Interface2 {}; struct MyService3 : public Interface1, public Interface2, public Interface3 {}; struct MyFactory1 : public us::ServiceFactory { std::map m_idToServiceMap; virtual us::InterfaceMap GetService(us::Module* module, const us::ServiceRegistrationBase& /*registration*/) { MyService1* s = new MyService1; m_idToServiceMap.insert(std::make_pair(module->GetModuleId(), s)); return us::MakeInterfaceMap(s); } virtual void UngetService(us::Module* module, const us::ServiceRegistrationBase& /*registration*/, const us::InterfaceMap& service) { std::map::iterator iter = m_idToServiceMap.find(module->GetModuleId()); if (iter != m_idToServiceMap.end()) { US_TEST_CONDITION(static_cast(iter->second) == us::ExtractInterface(service), "Compare service pointer") delete iter->second; m_idToServiceMap.erase(iter); } } }; struct MyFactory2 : public us::ServiceFactory { std::map m_idToServiceMap; virtual us::InterfaceMap GetService(us::Module* module, const us::ServiceRegistrationBase& /*registration*/) { MyService2* s = new MyService2; m_idToServiceMap.insert(std::make_pair(module->GetModuleId(), s)); return us::MakeInterfaceMap(s); } virtual void UngetService(us::Module* module, const us::ServiceRegistrationBase& /*registration*/, const us::InterfaceMap& service) { std::map::iterator iter = m_idToServiceMap.find(module->GetModuleId()); if (iter != m_idToServiceMap.end()) { US_TEST_CONDITION(static_cast(iter->second) == us::ExtractInterface(service), "Compare service pointer") delete iter->second; m_idToServiceMap.erase(iter); } } }; struct MyFactory3 : public us::ServiceFactory { std::map m_idToServiceMap; virtual us::InterfaceMap GetService(us::Module* module, const us::ServiceRegistrationBase& /*registration*/) { MyService3* s = new MyService3; m_idToServiceMap.insert(std::make_pair(module->GetModuleId(), s)); return us::MakeInterfaceMap(s); } virtual void UngetService(us::Module* module, const us::ServiceRegistrationBase& /*registration*/, const us::InterfaceMap& service) { std::map::iterator iter = m_idToServiceMap.find(module->GetModuleId()); if (iter != m_idToServiceMap.end()) { US_TEST_CONDITION(static_cast(iter->second) == us::ExtractInterface(service), "Compare service pointer") delete iter->second; m_idToServiceMap.erase(iter); } } }; US_USE_NAMESPACE int usServiceTemplateTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceTemplateTest"); ModuleContext* mc = GetModuleContext(); // Register compile tests MyService1 s1; MyService2 s2; MyService3 s3; us::ServiceRegistration sr1 = mc->RegisterService(&s1); us::ServiceRegistration sr2 = mc->RegisterService(&s2); us::ServiceRegistration sr3 = mc->RegisterService(&s3); MyFactory1 f1; us::ServiceRegistration sfr1 = mc->RegisterService(&f1); MyFactory2 f2; us::ServiceRegistration sfr2 = mc->RegisterService(static_cast(&f2)); MyFactory3 f3; us::ServiceRegistration sfr3 = mc->RegisterService(static_cast(&f3)); #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(mc->GetModule()->GetRegisteredServices().size() == 6, "# of reg services") #endif std::vector > s1refs = mc->GetServiceReferences(); US_TEST_CONDITION(s1refs.size() == 6, "# of interface1 regs") std::vector > s2refs = mc->GetServiceReferences(); US_TEST_CONDITION(s2refs.size() == 4, "# of interface2 regs") std::vector > s3refs = mc->GetServiceReferences(); US_TEST_CONDITION(s3refs.size() == 2, "# of interface3 regs") Interface1* i1 = mc->GetService(sr1.GetReference()); US_TEST_CONDITION(i1 == static_cast(&s1), "interface1 ptr") i1 = NULL; US_TEST_CONDITION(mc->UngetService(sr1.GetReference()), "unget interface1 ptr") i1 = mc->GetService(sfr1.GetReference()); US_TEST_CONDITION(i1 == static_cast(f1.m_idToServiceMap[mc->GetModule()->GetModuleId()]), "interface1 factory ptr") i1 = NULL; US_TEST_CONDITION(mc->UngetService(sfr1.GetReference()), "unget interface1 factory ptr") i1 = mc->GetService(sr2.GetReference(InterfaceType())); US_TEST_CONDITION(i1 == static_cast(&s2), "interface1 ptr") i1 = NULL; US_TEST_CONDITION(mc->UngetService(sr2.GetReference(InterfaceType())), "unget interface1 ptr") i1 = mc->GetService(sfr2.GetReference(InterfaceType())); US_TEST_CONDITION(i1 == static_cast(f2.m_idToServiceMap[mc->GetModule()->GetModuleId()]), "interface1 factory ptr") i1 = NULL; US_TEST_CONDITION(mc->UngetService(sfr2.GetReference(InterfaceType())), "unget interface1 factory ptr") Interface2* i2 = mc->GetService(sr2.GetReference(InterfaceType())); US_TEST_CONDITION(i2 == static_cast(&s2), "interface2 ptr") i2 = NULL; US_TEST_CONDITION(mc->UngetService(sr2.GetReference(InterfaceType())), "unget interface2 ptr") i2 = mc->GetService(sfr2.GetReference(InterfaceType())); US_TEST_CONDITION(i2 == static_cast(f2.m_idToServiceMap[mc->GetModule()->GetModuleId()]), "interface2 factory ptr") i2 = NULL; US_TEST_CONDITION(mc->UngetService(sfr2.GetReference(InterfaceType())), "unget interface2 factory ptr") i1 = mc->GetService(sr3.GetReference(InterfaceType())); US_TEST_CONDITION(i1 == static_cast(&s3), "interface1 ptr") i1 = NULL; US_TEST_CONDITION(mc->UngetService(sr3.GetReference(InterfaceType())), "unget interface1 ptr") i1 = mc->GetService(sfr3.GetReference(InterfaceType())); US_TEST_CONDITION(i1 == static_cast(f3.m_idToServiceMap[mc->GetModule()->GetModuleId()]), "interface1 factory ptr") i1 = NULL; US_TEST_CONDITION(mc->UngetService(sfr3.GetReference(InterfaceType())), "unget interface1 factory ptr") i2 = mc->GetService(sr3.GetReference(InterfaceType())); US_TEST_CONDITION(i2 == static_cast(&s3), "interface2 ptr") i2 = NULL; US_TEST_CONDITION(mc->UngetService(sr3.GetReference(InterfaceType())), "unget interface2 ptr") i2 = mc->GetService(sfr3.GetReference(InterfaceType())); US_TEST_CONDITION(i2 == static_cast(f3.m_idToServiceMap[mc->GetModule()->GetModuleId()]), "interface2 factory ptr") i2 = NULL; US_TEST_CONDITION(mc->UngetService(sfr3.GetReference(InterfaceType())), "unget interface2 factory ptr") Interface3* i3 = mc->GetService(sr3.GetReference(InterfaceType())); US_TEST_CONDITION(i3 == static_cast(&s3), "interface3 ptr") i3 = NULL; US_TEST_CONDITION(mc->UngetService(sr3.GetReference(InterfaceType())), "unget interface3 ptr") i3 = mc->GetService(sfr3.GetReference(InterfaceType())); US_TEST_CONDITION(i3 == static_cast(f3.m_idToServiceMap[mc->GetModule()->GetModuleId()]), "interface3 factory ptr") i3 = NULL; US_TEST_CONDITION(mc->UngetService(sfr3.GetReference(InterfaceType())), "unget interface3 factory ptr") sr1.Unregister(); sr2.Unregister(); sr3.Unregister(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usServiceTrackerTest.cpp b/Core/CppMicroServices/core/test/usServiceTrackerTest.cpp index 2a8665615b..7883d1cd42 100644 --- a/Core/CppMicroServices/core/test/usServiceTrackerTest.cpp +++ b/Core/CppMicroServices/core/test/usServiceTrackerTest.cpp @@ -1,284 +1,287 @@ -/*=================================================================== +/*============================================================================= -The Medical Imaging Interaction Toolkit (MITK) + Library: CppMicroServices -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -See LICENSE.txt or http://www.mitk.org for details. + http://www.apache.org/licenses/LICENSE-2.0 -===================================================================*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ #include #include #include #include #include #include #include #include #include "usServiceControlInterface.h" #include US_USE_NAMESPACE bool CheckConvertibility(const std::vector& refs, std::vector::const_iterator idBegin, std::vector::const_iterator idEnd) { std::vector ids; ids.assign(idBegin, idEnd); for (std::vector::const_iterator sri = refs.begin(); sri != refs.end(); ++sri) { for (std::vector::iterator idIter = ids.begin(); idIter != ids.end(); ++idIter) { if (sri->IsConvertibleTo(*idIter)) { ids.erase(idIter); break; } } } return ids.empty(); } struct MyInterfaceOne { virtual ~MyInterfaceOne() {} }; -US_DECLARE_SERVICE_INTERFACE(MyInterfaceOne, "org.cppmicroservices.servicetrackertest.MyInterfaceOne") struct MyInterfaceTwo { virtual ~MyInterfaceTwo() {} }; -US_DECLARE_SERVICE_INTERFACE(MyInterfaceTwo, "org.cppmicroservices.servicetrackertest.MyInterfaceTwo") class MyCustomizer : public us::ServiceTrackerCustomizer { public: MyCustomizer(us::ModuleContext* context) : m_context(context) {} virtual MyInterfaceOne* AddingService(const ServiceReferenceType& reference) { US_TEST_CONDITION_REQUIRED(reference, "AddingService() valid reference") return m_context->GetService(reference); } virtual void ModifiedService(const ServiceReferenceType& reference, MyInterfaceOne* service) { US_TEST_CONDITION(reference, "ModifiedService() valid reference") US_TEST_CONDITION(service, "ModifiedService() valid service") } virtual void RemovedService(const ServiceReferenceType& reference, MyInterfaceOne* service) { US_TEST_CONDITION(reference, "RemovedService() valid reference") US_TEST_CONDITION(service, "RemovedService() valid service") } private: us::ModuleContext* m_context; }; void TestFilterString() { us::ModuleContext* context = us::GetModuleContext(); MyCustomizer customizer(context); us::LDAPFilter filter("(" + us::ServiceConstants::SERVICE_ID() + ">=0)"); us::ServiceTracker tracker(context, filter, &customizer); tracker.Open(); struct MyServiceOne : public MyInterfaceOne {}; struct MyServiceTwo : public MyInterfaceTwo {}; MyServiceOne serviceOne; MyServiceTwo serviceTwo; ServiceRegistration reg1 = context->RegisterService(&serviceOne); ServiceRegistration reg2 = context->RegisterService(&serviceTwo); US_TEST_CONDITION(tracker.GetServiceReferences().size() == 1, "tracking count") reg1.Unregister(); reg2.Unregister(); } void TestServiceTracker() { #ifdef US_PLATFORM_WINDOWS const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif ModuleContext* mc = GetModuleContext(); SharedLibrary libS(LIB_PATH, "TestModuleS"); #ifdef US_BUILD_SHARED_LIBS // Start the test target to get a service published. try { libS.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() ); } #endif // 1. Create a ServiceTracker with ServiceTrackerCustomizer == null - std::string s1("org.cppmicroservices.TestModuleSService"); + std::string s1("us::TestModuleSService"); ServiceReferenceU servref = mc->GetServiceReference(s1 + "0"); - US_TEST_CONDITION_REQUIRED(servref != 0, "Test if registered service of id org.cppmicroservices.TestModuleSService0"); + US_TEST_CONDITION_REQUIRED(servref != 0, "Test if registered service of id us::TestModuleSService0"); ServiceReference servCtrlRef = mc->GetServiceReference(); US_TEST_CONDITION_REQUIRED(servCtrlRef != 0, "Test if constrol service was registered"); ServiceControlInterface* serviceController = mc->GetService(servCtrlRef); US_TEST_CONDITION_REQUIRED(serviceController != 0, "Test valid service controller"); std::auto_ptr > st1(new ServiceTracker(mc, servref)); // 2. Check the size method with an unopened service tracker US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Test if size == 0"); // 3. Open the service tracker and see what it finds, // expect to find one instance of the implementation, // "org.cppmicroservices.TestModuleSService0" st1->Open(); std::vector sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 1, "Checking ServiceTracker size"); US_TEST_CONDITION_REQUIRED(s1 + "0" == sa2[0].GetInterfaceId(), "Checking service implementation name"); // 5. Close this service tracker st1->Close(); // 6. Check the size method, now when the servicetracker is closed US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Checking ServiceTracker size"); // 7. Check if we still track anything , we should get null sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.empty(), "Checking ServiceTracker size"); // 8. A new Servicetracker, this time with a filter for the object std::string fs = std::string("(") + ServiceConstants::OBJECTCLASS() + "=" + s1 + "*" + ")"; LDAPFilter f1(fs); st1.reset(new ServiceTracker(mc, f1)); // add a service serviceController->ServiceControl(1, "register", 7); // 9. Open the service tracker and see what it finds, // expect to find two instances of references to // "org.cppmicroservices.TestModuleSService*" // i.e. they refer to the same piece of code std::vector ids; ids.push_back((s1 + "0")); ids.push_back((s1 + "1")); ids.push_back((s1 + "2")); ids.push_back((s1 + "3")); st1->Open(); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Checking service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+2), "Check for expected interface id [0]"); US_TEST_CONDITION_REQUIRED(sa2[1].IsConvertibleTo(s1 + "1"), "Check for expected interface id [1]"); // 10. Get libTestModuleS to register one more service and see if it appears serviceController->ServiceControl(2, "register", 1); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+3), "Check for expected interface id [2]"); // 11. Get libTestModuleS to register one more service and see if it appears serviceController->ServiceControl(3, "register", 2); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 4, "Checking service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.end()), "Check for expected interface id [3]"); // 12. Get libTestModuleS to unregister one service and see if it disappears serviceController->ServiceControl(3, "unregister", 0); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count"); // 13. Get the highest ranking service reference, it should have ranking 7 ServiceReferenceU h1 = st1->GetServiceReference(); int rank = any_cast(h1.GetProperty(ServiceConstants::SERVICE_RANKING())); US_TEST_CONDITION_REQUIRED(rank == 7, "Check service rank"); // 14. Get the service of the highest ranked service reference InterfaceMap o1 = st1->GetService(h1); US_TEST_CONDITION_REQUIRED(!o1.empty(), "Check for non-null service"); // 14a Get the highest ranked service, directly this time InterfaceMap o3 = st1->GetService(); US_TEST_CONDITION_REQUIRED(!o3.empty(), "Check for non-null service"); US_TEST_CONDITION_REQUIRED(o1 == o3, "Check for equal service instances"); // 15. Now release the tracking of that service and then try to get it // from the servicetracker, which should yield a null object serviceController->ServiceControl(1, "unregister", 7); InterfaceMap o2 = st1->GetService(h1); US_TEST_CONDITION_REQUIRED(o2.empty(), "Checkt that service is null"); // 16. Get all service objects this tracker tracks, it should be 2 std::vector ts1 = st1->GetServices(); US_TEST_CONDITION_REQUIRED(ts1.size() == 2, "Check service count"); // 17. Test the remove method. // First register another service, then remove it being tracked serviceController->ServiceControl(1, "register", 7); h1 = st1->GetServiceReference(); std::vector sa3 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa3.size() == 3, "Check service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa3, ids.begin(), ids.begin()+3), "Check for expected interface id [0]"); st1->Remove(h1); // remove tracking on one servref sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Check service reference count"); // 18. Test the addingService method,add a service reference // 19. Test the removedService method, remove a service reference // 20. Test the waitForService method InterfaceMap o9 = st1->WaitForService(50); US_TEST_CONDITION_REQUIRED(!o9.empty(), "Checking WaitForService method"); } int usServiceTrackerTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceTrackerTest") TestFilterString(); TestServiceTracker(); US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usStaticModuleResourceTest.cpp b/Core/CppMicroServices/core/test/usStaticModuleResourceTest.cpp index fd068d07c4..ab5985f005 100644 --- a/Core/CppMicroServices/core/test/usStaticModuleResourceTest.cpp +++ b/Core/CppMicroServices/core/test/usStaticModuleResourceTest.cpp @@ -1,157 +1,146 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include "usTestingMacros.h" #include "usTestingConfig.h" -#include +#include #include US_USE_NAMESPACE namespace { std::string GetResourceContent(const ModuleResource& resource) { std::string line; ModuleResourceStream rs(resource); std::getline(rs, line); return line; } struct ResourceComparator { bool operator()(const ModuleResource& mr1, const ModuleResource& mr2) const { return mr1 < mr2; } }; void testResourceOperators(Module* module) { std::vector resources = module->FindResources("", "res.txt", false); - US_TEST_CONDITION_REQUIRED(resources.size() == 2, "Check resource count") - US_TEST_CONDITION(resources[0] != resources[1], "Check non-equality for equally named resources") + US_TEST_CONDITION_REQUIRED(resources.size() == 1, "Check resource count") } void testResourcesWithStaticImport(Module* module) { ModuleResource resource = module->GetResource("res.txt"); US_TEST_CONDITION_REQUIRED(resource.IsValid(), "Check valid res.txt resource") std::string line = GetResourceContent(resource); US_TEST_CONDITION(line == "dynamic resource", "Check dynamic resource content") resource = module->GetResource("dynamic.txt"); US_TEST_CONDITION_REQUIRED(resource.IsValid(), "Check valid dynamic.txt resource") line = GetResourceContent(resource); US_TEST_CONDITION(line == "dynamic", "Check dynamic resource content") resource = module->GetResource("static.txt"); + US_TEST_CONDITION_REQUIRED(!resource.IsValid(), "Check in-valid static.txt resource") + + Module* importedByBModule = ModuleRegistry::GetModule("TestModuleImportedByB"); + US_TEST_CONDITION_REQUIRED(importedByBModule != NULL, "Check valid static module") + resource = importedByBModule->GetResource("static.txt"); US_TEST_CONDITION_REQUIRED(resource.IsValid(), "Check valid static.txt resource") line = GetResourceContent(resource); - US_TEST_CONDITION(line == "static", "Check dynamic resource content") + US_TEST_CONDITION(line == "static", "Check static resource content") std::vector resources = module->FindResources("", "*.txt", false); std::stable_sort(resources.begin(), resources.end(), ResourceComparator()); -#ifdef US_BUILD_SHARED_LIBS - US_TEST_CONDITION(resources.size() == 4, "Check imported resource count") + std::vector importedResources = importedByBModule->FindResources("", "*.txt", false); + std::stable_sort(importedResources.begin(), importedResources.end(), ResourceComparator()); + + US_TEST_CONDITION(resources.size() == 2, "Check resource count") + US_TEST_CONDITION(importedResources.size() == 2, "Check resource count") line = GetResourceContent(resources[0]); US_TEST_CONDITION(line == "dynamic", "Check dynamic.txt resource content") line = GetResourceContent(resources[1]); US_TEST_CONDITION(line == "dynamic resource", "Check res.txt (from importing module) resource content") - line = GetResourceContent(resources[2]); - US_TEST_CONDITION(line == "static resource", "Check res.txt (from imported module) resource content") - line = GetResourceContent(resources[3]); - US_TEST_CONDITION(line == "static", "Check static.txt (from importing module) resource content") -#else - US_TEST_CONDITION(resources.size() == 6, "Check imported resource count") - line = GetResourceContent(resources[0]); - US_TEST_CONDITION(line == "dynamic", "Check dynamic.txt resource content") - // skip foo.txt - line = GetResourceContent(resources[2]); - US_TEST_CONDITION(line == "dynamic resource", "Check res.txt (from importing module) resource content") - line = GetResourceContent(resources[3]); + line = GetResourceContent(importedResources[0]); US_TEST_CONDITION(line == "static resource", "Check res.txt (from imported module) resource content") - // skip special_chars.dummy.txt - line = GetResourceContent(resources[5]); + line = GetResourceContent(importedResources[1]); US_TEST_CONDITION(line == "static", "Check static.txt (from importing module) resource content") -#endif } } // end unnamed namespace int usStaticModuleResourceTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("StaticModuleResourceTest"); assert(GetModuleContext()); #ifdef US_BUILD_SHARED_LIBS #ifdef US_PLATFORM_WINDOWS const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif SharedLibrary libB(LIB_PATH, "TestModuleB"); try { libB.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } +#endif - Module* module = ModuleRegistry::GetModule("TestModuleB Module"); + Module* module = ModuleRegistry::GetModule("TestModuleB"); US_TEST_CONDITION_REQUIRED(module != NULL, "Test for existing module TestModuleB") - - US_TEST_CONDITION(module->GetName() == "TestModuleB Module", "Test module name") -#else - Module* module = GetModuleContext()->GetModule(); -#endif + US_TEST_CONDITION(module->GetName() == "TestModuleB", "Test module name") testResourceOperators(module); testResourcesWithStaticImport(module); -#ifdef US_BUILD_SHARED_LIBS - ModuleResource resource = module->GetResource("static.txt"); + ModuleResource resource = ModuleRegistry::GetModule("TestModuleImportedByB")->GetResource("static.txt"); US_TEST_CONDITION_REQUIRED(resource.IsValid(), "Check valid static.txt resource") - +#ifdef US_BUILD_SHARED_LIBS libB.Unload(); - - US_TEST_CONDITION_REQUIRED(resource.IsValid() == false, "Check invalid static.txt resource") + US_TEST_CONDITION_REQUIRED(resource.IsValid() == true, "Check still valid static.txt resource") #endif US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usStaticModuleTest.cpp b/Core/CppMicroServices/core/test/usStaticModuleTest.cpp index d158c6a742..187a8cf642 100644 --- a/Core/CppMicroServices/core/test/usStaticModuleTest.cpp +++ b/Core/CppMicroServices/core/test/usStaticModuleTest.cpp @@ -1,195 +1,183 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include "usTestUtilModuleListener.h" #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif // Load libTestModuleB and check that it exists and that the service it registers exists, // also check that the expected events occur void frame020a(ModuleContext* mc, TestModuleListener& listener, #ifdef US_BUILD_SHARED_LIBS SharedLibrary& libB) { try { libB.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } - - Module* moduleB = ModuleRegistry::GetModule("TestModuleB Module"); - US_TEST_CONDITION_REQUIRED(moduleB != 0, "Test for existing module TestModuleB") - - US_TEST_CONDITION(moduleB->GetName() == "TestModuleB Module", "Test module name") #else SharedLibrary& /*libB*/) { #endif + Module* moduleB = ModuleRegistry::GetModule("TestModuleB"); + US_TEST_CONDITION_REQUIRED(moduleB != 0, "Test for existing module TestModuleB") + + Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB"); + US_TEST_CONDITION_REQUIRED(moduleImportedByB != 0, "Test for existing module TestModuleImportedByB") + + US_TEST_CONDITION(moduleB->GetName() == "TestModuleB", "Test module name") + // Check if libB registered the expected service try { - std::vector refs = mc->GetServiceReferences("org.cppmicroservices.TestModuleBService"); + std::vector refs = mc->GetServiceReferences("us::TestModuleBService"); US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Test that both the service from the shared and imported library are regsitered"); InterfaceMap o1 = mc->GetService(refs.front()); US_TEST_CONDITION(!o1.empty(), "Test if first service object found"); InterfaceMap o2 = mc->GetService(refs.back()); US_TEST_CONDITION(!o2.empty(), "Test if second service object found"); try { US_TEST_CONDITION(mc->UngetService(refs.front()), "Test if Service UnGet for first service returns true"); US_TEST_CONDITION(mc->UngetService(refs.back()), "Test if Service UnGet for second service returns true"); } catch (const std::logic_error le) { US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what()) } // check the listeners for events std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleB)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleB)); + pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleImportedByB)); + pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleImportedByB)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.back())); seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.front())); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } catch (const ServiceException& /*se*/) { US_TEST_FAILED_MSG(<< "test module, expected service not found"); } #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleB->IsLoaded() == true, "Test if loaded correctly"); #endif } // Unload libB and check for correct events void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libB) { -#ifdef US_BUILD_SHARED_LIBS - Module* moduleB = ModuleRegistry::GetModule("TestModuleB Module"); + Module* moduleB = ModuleRegistry::GetModule("TestModuleB"); US_TEST_CONDITION_REQUIRED(moduleB != 0, "Test for non-null module") -#endif + + Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB"); + US_TEST_CONDITION_REQUIRED(moduleImportedByB != 0, "Test for non-null module") std::vector refs - = mc->GetServiceReferences("org.cppmicroservices.TestModuleBService"); + = mc->GetServiceReferences("us::TestModuleBService"); US_TEST_CONDITION(refs.front(), "Test for first valid service reference") US_TEST_CONDITION(refs.back(), "Test for second valid service reference") try { libB.Unload(); #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleB->IsLoaded() == false, "Test for unloaded state") #endif } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what()) } std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS + pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleImportedByB)); + pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleImportedByB)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleB)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleB)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS - seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.back())); seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.front())); + seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.back())); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } } // end unnamed namespace int usStaticModuleTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("StaticModuleTest"); ModuleContext* mc = GetModuleContext(); TestModuleListener listener; - try - { - mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged); - } - catch (const std::logic_error& ise) - { - US_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); - throw; - } - - try - { - mc->AddServiceListener(&listener, &TestModuleListener::ServiceChanged); - } - catch (const std::logic_error& ise) - { - US_TEST_OUTPUT( << "service listener registration failed " << ise.what() ); - throw; - } + ModuleListenerRegistrationHelper ml(mc, &listener, &TestModuleListener::ModuleChanged); + ServiceListenerRegistrationHelper sl(mc, &listener, &TestModuleListener::ServiceChanged); SharedLibrary libB(LIB_PATH, "TestModuleB"); frame020a(mc, listener, libB); frame030b(mc, listener, libB); - mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged); - mc->RemoveServiceListener(&listener, &TestModuleListener::ServiceChanged); - US_TEST_END() } diff --git a/Core/CppMicroServices/core/test/usTestManager.cpp b/Core/CppMicroServices/core/test/usTestDriverActivator.cpp similarity index 60% copy from Core/CppMicroServices/core/test/usTestManager.cpp copy to Core/CppMicroServices/core/test/usTestDriverActivator.cpp index def9cee003..c929f22ace 100644 --- a/Core/CppMicroServices/core/test/usTestManager.cpp +++ b/Core/CppMicroServices/core/test/usTestDriverActivator.cpp @@ -1,83 +1,67 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ -#include "usTestManager.h" - +#include "usTestDriverActivator.h" #include "usModuleImport.h" US_BEGIN_NAMESPACE -TestManager& TestManager::GetInstance() -{ - static TestManager instance; - return instance; -} - -void TestManager::Initialize() -{ - m_FailedTests = 0; - m_PassedTests = 0; -} +TestDriverActivator* TestDriverActivator::m_Instance = 0; -int TestManager::NumberOfFailedTests() +TestDriverActivator::TestDriverActivator() + : m_LoadCalled(false) { - return m_FailedTests; } -int TestManager::NumberOfPassedTests() +bool TestDriverActivator::LoadCalled() { - return m_PassedTests; + return m_Instance ? m_Instance->m_LoadCalled : false; } -void TestManager::TestFailed() +void TestDriverActivator::Load(ModuleContext*) { - m_FailedTests++; + this->m_Instance = this; + this->m_LoadCalled = true; } -void TestManager::TestPassed() +void TestDriverActivator::Unload(ModuleContext*) { - m_PassedTests++; + this->m_Instance = 0; } US_END_NAMESPACE +US_EXPORT_MODULE_ACTIVATOR(us::TestDriverActivator) + #ifndef US_BUILD_SHARED_LIBS US_IMPORT_MODULE(CppMicroServices) -US_IMPORT_MODULE_RESOURCES(CppMicroServices) US_IMPORT_MODULE(TestModuleA) US_IMPORT_MODULE(TestModuleA2) US_IMPORT_MODULE(TestModuleB) -US_IMPORT_MODULE_RESOURCES(TestModuleB) US_IMPORT_MODULE(TestModuleH) US_IMPORT_MODULE(TestModuleM) -US_IMPORT_MODULE_RESOURCES(TestModuleM) -US_IMPORT_MODULE(TestModuleR) -US_IMPORT_MODULE_RESOURCES(TestModuleR) +US_INITIALIZE_STATIC_MODULE(TestModuleR) US_IMPORT_MODULE(TestModuleS) US_IMPORT_MODULE(TestModuleSL1) US_IMPORT_MODULE(TestModuleSL3) US_IMPORT_MODULE(TestModuleSL4) - -US_LOAD_IMPORTED_MODULES_INTO_MAIN( - TestModuleA TestModuleA2 TestModuleB TestModuleImportedByB TestModuleH TestModuleM - TestModuleR TestModuleS TestModuleSL1 TestModuleSL3 TestModuleSL4 - ) +US_IMPORT_MODULE(main) #endif diff --git a/Core/CppMicroServices/core/test/modules/libRWithResources/usTestModuleR.cpp b/Core/CppMicroServices/core/test/usTestDriverActivator.h similarity index 73% copy from Core/CppMicroServices/core/test/modules/libRWithResources/usTestModuleR.cpp copy to Core/CppMicroServices/core/test/usTestDriverActivator.h index be8c0291ca..30a6cc2f4e 100644 --- a/Core/CppMicroServices/core/test/modules/libRWithResources/usTestModuleR.cpp +++ b/Core/CppMicroServices/core/test/usTestDriverActivator.h @@ -1,43 +1,49 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ +#ifndef USTESTDRIVERACTIVATOR_H +#define USTESTDRIVERACTIVATOR_H #include US_BEGIN_NAMESPACE -class TestModuleRActivator : public ModuleActivator +class TestDriverActivator : public ModuleActivator { public: - void Load(ModuleContext*) - { - } + TestDriverActivator(); - void Unload(ModuleContext*) - { - } + static bool LoadCalled(); + void Load(ModuleContext*); + + void Unload(ModuleContext* ); + +private: + + static TestDriverActivator* m_Instance; + bool m_LoadCalled; }; US_END_NAMESPACE -US_EXPORT_MODULE_ACTIVATOR(TestModuleR, US_PREPEND_NAMESPACE(TestModuleRActivator)) +#endif // USTESTDRIVERACTIVATOR_H diff --git a/Core/CppMicroServices/core/test/usTestManager.cpp b/Core/CppMicroServices/core/test/usTestManager.cpp index def9cee003..7bc5deadaf 100644 --- a/Core/CppMicroServices/core/test/usTestManager.cpp +++ b/Core/CppMicroServices/core/test/usTestManager.cpp @@ -1,83 +1,59 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestManager.h" -#include "usModuleImport.h" US_BEGIN_NAMESPACE TestManager& TestManager::GetInstance() { static TestManager instance; return instance; } void TestManager::Initialize() { m_FailedTests = 0; m_PassedTests = 0; } int TestManager::NumberOfFailedTests() { return m_FailedTests; } int TestManager::NumberOfPassedTests() { return m_PassedTests; } void TestManager::TestFailed() { m_FailedTests++; } void TestManager::TestPassed() { m_PassedTests++; } US_END_NAMESPACE - -#ifndef US_BUILD_SHARED_LIBS -US_IMPORT_MODULE(CppMicroServices) -US_IMPORT_MODULE_RESOURCES(CppMicroServices) -US_IMPORT_MODULE(TestModuleA) -US_IMPORT_MODULE(TestModuleA2) -US_IMPORT_MODULE(TestModuleB) -US_IMPORT_MODULE_RESOURCES(TestModuleB) -US_IMPORT_MODULE(TestModuleH) -US_IMPORT_MODULE(TestModuleM) -US_IMPORT_MODULE_RESOURCES(TestModuleM) -US_IMPORT_MODULE(TestModuleR) -US_IMPORT_MODULE_RESOURCES(TestModuleR) -US_IMPORT_MODULE(TestModuleS) -US_IMPORT_MODULE(TestModuleSL1) -US_IMPORT_MODULE(TestModuleSL3) -US_IMPORT_MODULE(TestModuleSL4) - -US_LOAD_IMPORTED_MODULES_INTO_MAIN( - TestModuleA TestModuleA2 TestModuleB TestModuleImportedByB TestModuleH TestModuleM - TestModuleR TestModuleS TestModuleSL1 TestModuleSL3 TestModuleSL4 - ) -#endif diff --git a/Core/CppMicroServices/core/test/usTestUtilModuleListener.h b/Core/CppMicroServices/core/test/usTestUtilModuleListener.h index b8a75c8647..dd3e313244 100644 --- a/Core/CppMicroServices/core/test/usTestUtilModuleListener.h +++ b/Core/CppMicroServices/core/test/usTestUtilModuleListener.h @@ -1,66 +1,141 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USTESTUTILMODULELISTENER_H #define USTESTUTILMODULELISTENER_H #include "usModuleEvent.h" #include "usServiceEvent.h" +#include "usModuleContext.h" + +#include "usTestingMacros.h" US_BEGIN_NAMESPACE class ModuleContext; class TestModuleListener { public: TestModuleListener(); void ModuleChanged(const ModuleEvent event); void ServiceChanged(const ServiceEvent event); ModuleEvent GetModuleEvent() const; ServiceEvent GetServiceEvent() const; bool CheckListenerEvents( bool pexp, ModuleEvent::Type ptype, bool sexp, ServiceEvent::Type stype, Module* moduleX, ServiceReferenceU* servX); bool CheckListenerEvents(const std::vector& pEvts); bool CheckListenerEvents(const std::vector& seEvts); bool CheckListenerEvents(const std::vector& pEvts, const std::vector& seEvts); private: std::vector serviceEvents; std::vector moduleEvents; }; +template +class ModuleListenerRegistrationHelper +{ + +public: + + typedef void(Receiver::*CallbackType)(const ModuleEvent); + + ModuleListenerRegistrationHelper(ModuleContext* context, Receiver* receiver, CallbackType callback) + : context(context) + , receiver(receiver) + , callback(callback) + { + try + { + context->AddModuleListener(receiver, callback); + } + catch (const std::logic_error& ise) + { + US_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); + throw; + } + } + + ~ModuleListenerRegistrationHelper() + { + context->RemoveModuleListener(receiver, callback); + } + +private: + + ModuleContext* context; + Receiver* receiver; + CallbackType callback; +}; + +template +class ServiceListenerRegistrationHelper +{ + +public: + + typedef void(Receiver::*CallbackType)(const ServiceEvent); + + ServiceListenerRegistrationHelper(ModuleContext* context, Receiver* receiver, CallbackType callback) + : context(context) + , receiver(receiver) + , callback(callback) + { + try + { + context->AddServiceListener(receiver, callback); + } + catch (const std::logic_error& ise) + { + US_TEST_OUTPUT( << "service listener registration failed " << ise.what() ); + throw; + } + } + + ~ServiceListenerRegistrationHelper() + { + context->RemoveServiceListener(receiver, callback); + } + +private: + + ModuleContext* context; + Receiver* receiver; + CallbackType callback; +}; + US_END_NAMESPACE #endif // USTESTUTILMODULELISTENER_H diff --git a/Core/CppMicroServices/core/test/usTestingMacros.h b/Core/CppMicroServices/core/test/usTestingMacros.h index 44ec95e0d5..b7a2b2d04b 100644 --- a/Core/CppMicroServices/core/test/usTestingMacros.h +++ b/Core/CppMicroServices/core/test/usTestingMacros.h @@ -1,133 +1,138 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ +#ifndef USTESTINGMACROS_H_ +#define USTESTINGMACROS_H_ + #include #include #include #include #include "usTestManager.h" US_BEGIN_NAMESPACE /** \brief Indicate a failed test. */ class TestFailedException : public std::exception { public: TestFailedException() {} }; US_END_NAMESPACE /** * * \brief Output some text without generating a terminating newline. * * */ #define US_TEST_OUTPUT_NO_ENDL(x) \ std::cout x << std::flush; /** \brief Output some text. */ #define US_TEST_OUTPUT(x) \ US_TEST_OUTPUT_NO_ENDL(x << "\n") /** \brief Do some general test preparations. Must be called first in the main test function. */ #define US_TEST_BEGIN(testName) \ std::string usTestName(#testName); \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().Initialize(); \ try { /** \brief Fail and finish test with message MSG */ #define US_TEST_FAILED_MSG(MSG) \ US_TEST_OUTPUT(MSG) \ throw US_PREPEND_NAMESPACE(TestFailedException)(); /** \brief Must be called last in the main test function. */ #define US_TEST_END() \ } catch (const US_PREPEND_NAMESPACE(TestFailedException)&) { \ US_TEST_OUTPUT(<< "Further test execution skipped.") \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \ } catch (const std::exception& ex) { \ US_TEST_OUTPUT(<< "Exception occured " << ex.what()) \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \ } \ if (US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfFailedTests() > 0) { \ US_TEST_OUTPUT(<< usTestName << ": [DONE FAILED] , subtests passed: " << \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfPassedTests() << " failed: " << \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfFailedTests() ) \ return EXIT_FAILURE; \ } else { \ US_TEST_OUTPUT(<< usTestName << ": " \ << US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfPassedTests() \ << " tests [DONE PASSED]") \ return EXIT_SUCCESS; \ } #define US_TEST_CONDITION(COND,MSG) \ US_TEST_OUTPUT_NO_ENDL(<< MSG) \ if ( ! (COND) ) { \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \ US_TEST_OUTPUT(<< " [FAILED]\n" << "In " << __FILE__ \ << ", line " << __LINE__ \ << ": " #COND " : [FAILED]") \ } else { \ US_TEST_OUTPUT(<< " [PASSED]") \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \ } #define US_TEST_CONDITION_REQUIRED(COND,MSG) \ US_TEST_OUTPUT_NO_ENDL(<< MSG) \ if ( ! (COND) ) { \ US_TEST_FAILED_MSG(<< " [FAILED]\n" << " +--> in " << __FILE__ \ << ", line " << __LINE__ \ << ", expression is false: \"" #COND "\"") \ } else { \ US_TEST_OUTPUT(<< " [PASSED]") \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \ } /** * \brief Begin block which should be checked for exceptions * * This macro, together with US_TEST_FOR_EXCEPTION_END, can be used * to test whether a code block throws an expected exception. The test FAILS if the * exception is NOT thrown. */ #define US_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ try { #define US_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \ US_TEST_OUTPUT( << "Expected an '" << #EXCEPTIONCLASS << "' exception. [FAILED]") \ } \ catch (EXCEPTIONCLASS) { \ US_TEST_OUTPUT(<< "Caught an expected '" << #EXCEPTIONCLASS \ << "' exception. [PASSED]") \ US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \ } /** * \brief Simplified version of US_TEST_FOR_EXCEPTION_BEGIN / END for * a single statement */ #define US_TEST_FOR_EXCEPTION(EXCEPTIONCLASS, STATEMENT) \ US_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \ STATEMENT ; \ US_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) + +#endif // USTESTINGMACROS_H_ diff --git a/Core/CppMicroServices/doc/CMakeLists.txt b/Core/CppMicroServices/doc/CMakeLists.txt index a12f7b2599..3e0e7613c5 100644 --- a/Core/CppMicroServices/doc/CMakeLists.txt +++ b/Core/CppMicroServices/doc/CMakeLists.txt @@ -1,77 +1,78 @@ if(NOT US_IS_EMBEDDED AND NOT US_NO_DOCUMENTATION) find_package(Doxygen) if(DOXYGEN_FOUND) option(US_DOCUMENTATION_FOR_WEBPAGE "Build Doxygen documentation for the webpage" OFF) set(US_DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR} CACHE PATH "Doxygen output directory") mark_as_advanced(US_DOCUMENTATION_FOR_WEBPAGE US_DOXYGEN_OUTPUT_DIR) set(US_HAVE_DOT "NO") if(DOXYGEN_DOT_EXECUTABLE) set(US_HAVE_DOT "YES") endif() if(NOT DEFINED US_DOXYGEN_DOT_NUM_THREADS) set(US_DOXYGEN_DOT_NUM_THREADS 4) endif() # We are in standalone mode, so we generate a "mainpage" set(US_DOXYGEN_MAIN_PAGE_CMD "\\mainpage") set(US_DOXYGEN_ENABLED_SECTIONS "us_standalone") if(US_DOCUMENTATION_FOR_WEBPAGE) configure_file(doxygen/header.html ${CMAKE_CURRENT_BINARY_DIR}/header.html COPY_ONLY) set(US_DOXYGEN_HEADER header.html) configure_file(doxygen/footer.html ${CMAKE_CURRENT_BINARY_DIR}/footer.html COPY_ONLY) set(US_DOXYGEN_FOOTER footer.html) configure_file(doxygen/doxygen_extra.css ${CMAKE_CURRENT_BINARY_DIR}/doxygen_extra.css COPY_ONLY) set(US_DOXYGEN_EXTRA_CSS doxygen_extra.css) set(US_DOXYGEN_OUTPUT_DIR ${PROJECT_SOURCE_DIR}/gh-pages) if(${PROJECT_NAME}_MINOR_VERSION EQUAL 99) set(US_DOXYGEN_HTML_OUTPUT "doc_latest") else() set(US_DOXYGEN_HTML_OUTPUT "doc_${${PROJECT_NAME}_MAJOR_VERSION}_${${PROJECT_NAME}_MINOR_VERSION}") endif() else() set(US_DOXYGEN_HEADER ) set(US_DOXYGEN_FOOTER ) set(US_DOXYGEN_CSS ) set(US_DOXYGEN_HTML_OUTPUT "html") endif() # Compile a command line tool which transforms comments in CMake scripts into # Doxygen parseable C code. set(CMakeDoxygenFilter_EXECUTABLE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CMakeDoxygenFilter${CMAKE_EXECUTABLE_SUFFIX}") try_compile(_result_var "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/CMakeDoxygenFilter.cpp" OUTPUT_VARIABLE _compile_output COPY_FILE ${CMakeDoxygenFilter_EXECUTABLE} ) if(NOT _result_var) message(FATAL_ERROR "error: Faild to compile ${CMAKE_CURRENT_SOURCE_DIR}/CMakeDoxygenFilter.cpp (result: ${result_var})\n${_compile_output}") endif() configure_file(doxygen.conf.in ${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf) add_custom_target(doc ${DOXYGEN} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + VERBATIM ) install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build \"${PROJECT_BINARY_DIR}\" --target doc)") - install(DIRECTORY ${US_DOXYGEN_OUTPUT_DIR}/${US_DOXYGEN_HTML_OUTPUT} - DESTINATION ${AUXILIARY_INSTALL_DIR}/doc/ + install(DIRECTORY "${US_DOXYGEN_OUTPUT_DIR}/${US_DOXYGEN_HTML_OUTPUT}" + DESTINATION "${AUXILIARY_INSTALL_DIR}/doc/" COMPONENT doc) endif() endif() diff --git a/Core/CppMicroServices/doc/doxygen.conf.in b/Core/CppMicroServices/doc/doxygen.conf.in index 5caf947d09..85ebf73053 100644 --- a/Core/CppMicroServices/doc/doxygen.conf.in +++ b/Core/CppMicroServices/doc/doxygen.conf.in @@ -1,2305 +1,2306 @@ # Doxyfile 1.8.5 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "C++ Micro Services" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @CppMicroServices_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "A dynamic OSGi-like C++ service registry" # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = "@US_DOXYGEN_OUTPUT_DIR@" # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- # Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, # Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, # Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 2 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = "FIXME=\par Fix Me's:\n" \ "embmainpage{1}=@US_DOXYGEN_MAIN_PAGE_CMD@" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = YES # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = YES # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = @US_DOXYGEN_ENABLED_SECTIONS@ # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 0 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = NO # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = "@PROJECT_SOURCE_DIR@" \ - "@PROJECT_SOURCE_DIR@/cmake/usFunctionEmbedResources.cmake" \ + "@PROJECT_SOURCE_DIR@/cmake/usFunctionAddResources.cmake" \ "@PROJECT_SOURCE_DIR@/cmake/usFunctionGenerateModuleInit.cmake" \ - "@PROJECT_SOURCE_DIR@/cmake/usFunctionGenerateExecutableInit.cmake" \ - "@PROJECT_BINARY_DIR@/include/usGlobalConfig.h" + "@PROJECT_BINARY_DIR@/include/usGlobalConfig.h" \ + "@PROJECT_BINARY_DIR@/core/include" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = *.h \ *.dox \ *.md # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = "@PROJECT_SOURCE_DIR@/README.md" \ "@PROJECT_SOURCE_DIR@/gh-pages/" \ + "@PROJECT_SOURCE_DIR@/third_party/" \ "@PROJECT_SOURCE_DIR@/.git/" # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */test/* \ */snippets/* \ - */examples/* \ + */core/examples/* \ */.git/* \ *_p.h \ *Private.* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = us \ US_NAMESPACE \ *Private* \ ModuleInfo \ ServiceObjectsBase* \ TrackedService* # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@/core/doc/snippets/" \ "@PROJECT_SOURCE_DIR@/core/examples/" # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = *.cmake=@CMakeDoxygenFilter_EXECUTABLE@ # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 3 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = "@US_DOXYGEN_HTML_OUTPUT@" # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = "@US_DOXYGEN_HEADER@" # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = "@US_DOXYGEN_FOOTER@" # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = "@US_DOXYGEN_EXTRA_CSS@" # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 300 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /